Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / WinForms, .Net Framework Новый топик    Ответить
Топик располагается на нескольких страницах: [1] 2   вперед  Ctrl      все
 Асинхронное блокирование ресурса по значению  [new]
hVostt
Member

Откуда:
Сообщений: 15822
Покритикуйте код, реализующий асинхронное блокирование ресурса по значению.

Пример использования:

private static readonly Synchronizer<int> Sync = new Synchronizer<int>();

...

public async Task SomeWork(SomeModel model, CancellationToken cancellationToken)
{
    // блокируем, чтобы метод выполнялся монопольно для каждого model.Id
    using(await Sync.Lock(model.Id, cancellationToken)
    {
        ...
    }
}


Реализация.

    public class Synchronizer<T>
    {
        private readonly object _lock = new object();
        private readonly Dictionary<T, SyncLock<T>> _syncLocks = new Dictionary<T, SyncLock<T>>();

        public async Task<IDisposable> LockAsync(T value, CancellationToken cancellationToken)
        {
            SyncLock<T> syncLock;
            lock (_lock)
            {
                if (!_syncLocks.TryGetValue(value, out syncLock))
                {
                    syncLock = new SyncLock<T>(this, value);
                    _syncLocks.Add(value, syncLock);
                }
                syncLock.Capture();
            }
            await syncLock.WaitAsync(cancellationToken).ConfigureAwait(false);
            return syncLock;
        }

        internal void Unlock(SyncLock<T> syncLock)
        {
            lock (_lock)
            {
                var capturedCount = syncLock.Release();
                if (capturedCount == 0) _syncLocks.Remove(syncLock.Value);
            }
        }
    }

    internal class SyncLock<T> : IDisposable
    {
        private readonly Synchronizer<T> _synchronizer;
        private readonly T _value;
        private readonly SemaphoreSlim _semaphore;
        private int _captureCount;


        #region Internal routines

        internal SyncLock([NotNull] Synchronizer<T> synchronizer, T value)
        {
            _synchronizer = synchronizer;
            _value = value;
            _semaphore = new SemaphoreSlim(1, 1);
        }

        internal Task WaitAsync(CancellationToken cancellationToken)
        {
            return _semaphore.WaitAsync(cancellationToken);
        }

        internal int Capture()
        {
            return ++_captureCount;
        }

        internal int Release()
        {
            _captureCount--;
            _semaphore.Release();
            return _captureCount;
        }

        #endregion


        #region Properties

        public T Value => _value;

        #endregion


        #region IDisposable

        /// <inheritdoc />
        public void Dispose()
        {
            _synchronizer.Unlock(this);
        }

        #endregion
    }
21 авг 18, 11:13    [21649123]     Ответить | Цитировать Сообщить модератору
 Re: Асинхронное блокирование ресурса по значению  [new]
WaspNewCore
Member

Откуда:
Сообщений: 147
hVostt

    // блокируем, чтобы метод выполнялся монопольно для каждого model.Id
    using(await Sync.Lock(model.Id, cancellationToken)
    {
        ...
    }
}



Это к примеру, но могу порекомендовать посмотреть на эту либу
https://github.com/StephenCleary/AsyncEx
Там много вспомогательных примитивов для работы с асинхронностью, от большого спеца по асинхронности (я по его книге как раз и изучал тему асинхронности - очень понятно все разъясняет).
В частности там есть и AsyncLock, с которым можно работать как в асинхронном режиме, так и в асинхронном.
https://github.com/StephenCleary/AsyncEx/tree/v4
23 авг 18, 14:57    [21652132]     Ответить | Цитировать Сообщить модератору
 Re: Асинхронное блокирование ресурса по значению  [new]
ViPRos
Member

Откуда:
Сообщений: 9568
hVostt,

Dispose может затянуться
23 авг 18, 15:26    [21652163]     Ответить | Цитировать Сообщить модератору
 Re: Асинхронное блокирование ресурса по значению  [new]
hVostt
Member

Откуда:
Сообщений: 15822
WaspNewCore
Это к примеру, но могу порекомендовать посмотреть на эту либу
https://github.com/StephenCleary/AsyncEx
Там много вспомогательных примитивов для работы с асинхронностью, от большого спеца по асинхронности (я по его книге как раз и изучал тему асинхронности - очень понятно все разъясняет).


Честно говоря, мне очень сильно не нравится код из этого репозитория, я полностью избегаю его использовать. Хотя всё, что там реализовано работает, и работает правильно, большие вопросы к эффективности реализации.

Ну и соответствующая статья есть на хабре, как найду, приложу сюда.

WaspNewCore
В частности там есть и AsyncLock, с которым можно работать как в асинхронном режиме, так и в асинхронном.
https://github.com/StephenCleary/AsyncEx/tree/v4


Ну в моём коде тоже нормальный асинк лок на SemaphoreSlim, но я с радостью готов услышать, почему он здесь будет работать не эффективно.
23 авг 18, 15:42    [21652184]     Ответить | Цитировать Сообщить модератору
 Re: Асинхронное блокирование ресурса по значению  [new]
hVostt
Member

Откуда:
Сообщений: 15822
ViPRos
hVostt,

Dispose может затянуться


Да, ведь там и происходит блокирование :)

А где ты видишь здесь проблему?
23 авг 18, 15:43    [21652186]     Ответить | Цитировать Сообщить модератору
 Re: Асинхронное блокирование ресурса по значению  [new]
ViPRos
Member

Откуда:
Сообщений: 9568
hVostt,

там идет разблокирование, а временем диспоз ты не управляешь
23 авг 18, 15:47    [21652193]     Ответить | Цитировать Сообщить модератору
 Re: Асинхронное блокирование ресурса по значению  [new]
hVostt
Member

Откуда:
Сообщений: 15822
ViPRos,

Важны гарантии. Диспоз тут для удобства, чтобы не делать try/catch/finally.
23 авг 18, 16:14    [21652234]     Ответить | Цитировать Сообщить модератору
 Re: Асинхронное блокирование ресурса по значению  [new]
ViPRos
Member

Откуда:
Сообщений: 9568
hVostt,

Надо как можно быстрее освободить ресурс. А так как хошь :)
23 авг 18, 16:40    [21652259]     Ответить | Цитировать Сообщить модератору
 Re: Асинхронное блокирование ресурса по значению  [new]
ViPRos
Member

Откуда:
Сообщений: 9568
ViPRos,

а try/catch там аж два раза для monitor вызывается
23 авг 18, 16:51    [21652264]     Ответить | Цитировать Сообщить модератору
 Re: Асинхронное блокирование ресурса по значению  [new]
ViPRos
Member

Откуда:
Сообщений: 9568
в общем плохой, медленный, нерабочий код
23 авг 18, 16:54    [21652266]     Ответить | Цитировать Сообщить модератору
 Re: Асинхронное блокирование ресурса по значению  [new]
hVostt
Member

Откуда:
Сообщений: 15822
ViPRos
ViPRos,

а try/catch там аж два раза для monitor вызывается


lock только при операциях со словарём, т.е. очень быстро. Я не знаю как это сделать ещё быстрее, ConcurrentDictionary тут не нужен, так как мне ещё счётчик надо инкрементить/декрементить атомарно совместно с извлечением записи из словаря.

ViPRos
в общем плохой, медленный, нерабочий код


А как сделать быстрее? Мне нужен именно async lock ресурса.
23 авг 18, 18:42    [21652335]     Ответить | Цитировать Сообщить модератору
 Re: Асинхронное блокирование ресурса по значению  [new]
ViPRos
Member

Откуда:
Сообщений: 9568
hVostt,

я, к сожалению, с async не работаю.
Просто всегда блокировка точечная операция и ее так обрамлять плохая идея.
А синхронную версию типа твоего я тут где то уже выкладывал и ты его видел.
23 авг 18, 19:38    [21652389]     Ответить | Цитировать Сообщить модератору
 Re: Асинхронное блокирование ресурса по значению  [new]
Dima T
Member

Откуда:
Сообщений: 13921
hVostt
ViPRos
ViPRos,

а try/catch там аж два раза для monitor вызывается


lock только при операциях со словарём, т.е. очень быстро. Я не знаю как это сделать ещё быстрее

Ты неправильно размышляешь. Ты сделал быстро распараллелив разные ресурсы, а насколько быстро происходит распараллеливание это вторично. Это обслуживающий код, и то что он обслуживает намного тяжелее его. В остальном быстрее monitor, т.е. критической секции внутри, только спинлоки, но они тормозят когда ядер проца не хватает.

Еще небольшой плюсик можешь поиметь при разведении данных разных потоков по разным кэш-линиям 20429620
23 авг 18, 20:02    [21652414]     Ответить | Цитировать Сообщить модератору
 Re: Асинхронное блокирование ресурса по значению  [new]
hVostt
Member

Откуда:
Сообщений: 15822
ViPRos
Просто всегда блокировка точечная операция и ее так обрамлять плохая идея.


Ты имеешь в виду using? Почему?

ViPRos
А синхронную версию типа твоего я тут где то уже выкладывал и ты его видел.


Синхронную видел :) Но так как щас всё асинках, код приходится переписывать.
23 авг 18, 20:18    [21652427]     Ответить | Цитировать Сообщить модератору
 Re: Асинхронное блокирование ресурса по значению  [new]
hVostt
Member

Откуда:
Сообщений: 15822
Dima T
Ты неправильно размышляешь. Ты сделал быстро распараллелив разные ресурсы, а насколько быстро происходит распараллеливание это вторично. Это обслуживающий код, и то что он обслуживает намного тяжелее его. В остальном быстрее monitor, т.е. критической секции внутри, только спинлоки, но они тормозят когда ядер проца не хватает.


Монитор нельзя использовать в контексте асинхронной операции, так как он лочит поток, а надо лочить ресурс, да и C# не скомпилирует await внутри критической секции, именно по этой причине.

Тут как бы не про распараллеливание идёт речь, а про асинхронность.

Dima T
Еще небольшой плюсик можешь поиметь при разведении данных разных потоков по разным кэш-линиям 20429620


В этом имеет смысл, если в кеш-линию попадает то, что процессор может предсказать. Например, при последовательной обработке массива данных, нехорошо будет если будет переключение не на границе линии. Но тут я не большой эксперт, лоу перфоманс только недавно меня стал интересовать :)
23 авг 18, 20:23    [21652429]     Ответить | Цитировать Сообщить модератору
 Re: Асинхронное блокирование ресурса по значению  [new]
Dima T
Member

Откуда:
Сообщений: 13921
hVostt
Dima T
Ты неправильно размышляешь. Ты сделал быстро распараллелив разные ресурсы, а насколько быстро происходит распараллеливание это вторично. Это обслуживающий код, и то что он обслуживает намного тяжелее его. В остальном быстрее monitor, т.е. критической секции внутри, только спинлоки, но они тормозят когда ядер проца не хватает.


Монитор нельзя использовать в контексте асинхронной операции, так как он лочит поток, а надо лочить ресурс, да и C# не скомпилирует await внутри критической секции, именно по этой причине.

Почему? Ты внутри lock делаешь очень легкие операции, все по фэншую. Возможно я что-то не понял в твоем коде, но в целом криминала не вижу.

hVostt
Тут как бы не про распараллеливание идёт речь, а про асинхронность.

Ты разработчик виндовса и .net ? Если нет, то речь про распараллеливание. "асинхронность" это продвинутое распараллеливание от разработчиков ОС.

hVostt
Dima T
Еще небольшой плюсик можешь поиметь при разведении данных разных потоков по разным кэш-линиям 20429620


В этом имеет смысл, если в кеш-линию попадает то, что процессор может предсказать. Например, при последовательной обработке массива данных, нехорошо будет если будет переключение не на границе линии. Но тут я не большой эксперт, лоу перфоманс только недавно меня стал интересовать :)

Об этом Рихтер упоминал, разведи на 64 байта счетчики разных потоков и считать они будут намного быстрее.
23 авг 18, 20:35    [21652444]     Ответить | Цитировать Сообщить модератору
 Re: Асинхронное блокирование ресурса по значению  [new]
hVostt
Member

Откуда:
Сообщений: 15822
Dima T
Почему? Ты внутри lock делаешь очень легкие операции, все по фэншую. Возможно я что-то не понял в твоем коде, но в целом криминала не вижу.


Ок, спасибо :)

Dima T
Ты разработчик виндовса и .net ? Если нет, то речь про распараллеливание. "асинхронность" это продвинутое распараллеливание от разработчиков ОС.


Ну если считать IO-bound отдельным видом потоков, то да :)

Dima T
Об этом Рихтер упоминал, разведи на 64 байта счетчики разных потоков и считать они будут намного быстрее.


А, теперь понял о чём ты :)) StructLayout+FieldOffset?
23 авг 18, 20:40    [21652447]     Ответить | Цитировать Сообщить модератору
 Re: Асинхронное блокирование ресурса по значению  [new]
Dima T
Member

Откуда:
Сообщений: 13921
hVostt
А, теперь понял о чём ты :)) StructLayout+FieldOffset?

Суть в том что если две переменных хранятся в одной кэшлинии, например
struct {
  int x;
  int y;
}

и два разных потока делают x++ и y++ соответственно, то это будет раз в 7-8 медленнее чем так
struct {
  int x;
  byte buffer[64];
  int y;
}

Но это не точно, пишу по аналогии с С/С++, не уверен что в C# byte buffer[64] это массив внутри структуры, а не указатель на него.
23 авг 18, 20:50    [21652454]     Ответить | Цитировать Сообщить модератору
 Re: Асинхронное блокирование ресурса по значению  [new]
Dima T
Member

Откуда:
Сообщений: 13921
hVostt
Ну если считать IO-bound отдельным видом потоков, то да :)

Дай ссылку почитать про это. Может я чего не знаю.
23 авг 18, 21:14    [21652463]     Ответить | Цитировать Сообщить модератору
 Re: Асинхронное блокирование ресурса по значению  [new]
hVostt
Member

Откуда:
Сообщений: 15822
Dima T
Но это не точно, пишу по аналогии с С/С++, не уверен что в C# byte buffer[64] это массив внутри структуры, а не указатель на него.


Ну то есть так

[StructLayout(LayoutKind.Explicit)]
public struct struct1
{
    [FieldOffset(0)]
    public int x; 
    [FieldOffset(64)]
    public int y; 
}


byte buffer[64], кстати, не сдвинет позицию на 64 байта. Ну лан, это лирика :) Суть ясна.


Dima T
Дай ссылку почитать про это. Может я чего не знаю.


Наверное знаешь, на всякий

https://docs.microsoft.com/en-US/windows/desktop/FileIO/i-o-completion-ports
https://blog.stephencleary.com/2013/11/there-is-no-thread.html
23 авг 18, 21:59    [21652493]     Ответить | Цитировать Сообщить модератору
 Re: Асинхронное блокирование ресурса по значению  [new]
mikron
Member

Откуда: Germany / Stuttgart
Сообщений: 809
hVostt
Покритикуйте код, реализующий асинхронное блокирование ресурса по значению.

В традиции и образе ресурса: код - гавно, афтор - му**к.
Да и кстати: куда уехал цирк? где ПТ?

По пунктам:
- кацелейшн как то странно выглядик. Если его словили то можно и без лока двигать, да ?
- overengenered. Можно и нужно просче. SemaphoreSlim не нужен.
24 авг 18, 14:28    [21653190]     Ответить | Цитировать Сообщить модератору
 Re: Асинхронное блокирование ресурса по значению  [new]
hVostt
Member

Откуда:
Сообщений: 15822
mikron
- кацелейшн как то странно выглядик. Если его словили то можно и без лока двигать, да ?


Хорошее замечание, не хватает проверки на IsCanceled, хотя внутренняя реализация его должна тоже схавать, но здесь проверки не хватает.

Вообще, await lock в using уже не выглядит хорошим решением.

mikron
- overengenered. Можно и нужно просче. SemaphoreSlim не нужен.


Другие варианты async lock?
24 авг 18, 14:42    [21653206]     Ответить | Цитировать Сообщить модератору
 Re: Асинхронное блокирование ресурса по значению  [new]
hVostt
Member

Откуда:
Сообщений: 15822
Следующая версия :)
Изменился способ использования

private static readonly Synchronizer<int> Sync = new Synchronizer<int>();

...

public async Task SomeWork(SomeModel model, CancellationToken cancellationToken)
{
    // получаем объект блокировки для монопольных операций по model.Id
    using(var syncLock = await Sync.GetLock(model.Id))
    {
        // блокируем
        await syncLock.WaitAsync(cancellationToken);
        // делаем свои дела
        ...
    }
}


    public class Synchronizer<T>
    {
        private readonly object _lock = new object();
        private readonly Dictionary<T, SyncLock<T>> _syncLocks = new Dictionary<T, SyncLock<T>>();

        public SyncLock<T> GetLock(T value)
        {
            lock (_lock)
            {
                if (!_syncLocks.TryGetValue(value, out var syncLock))
                {
                    syncLock = new SyncLock<T>(this, value);
                    _syncLocks.Add(value, syncLock);
                }
                syncLock.Capture();
                return syncLock;
            }
        }

        internal void Unlock(SyncLock<T> syncLock)
        {
            lock (_lock)
            {
                var capturedCount = syncLock.Release();
                if (capturedCount == 0) _syncLocks.Remove(syncLock.Value);
            }
        }
    }

    public class SyncLock<T> : IDisposable
    {
        private readonly Synchronizer<T> _synchronizer;
        private readonly T _value;
        private readonly SemaphoreSlim _semaphore;
        private int _captureCount;


        #region Internal routines

        internal SyncLock([NotNull] Synchronizer<T> synchronizer, T value)
        {
            _synchronizer = synchronizer;
            _value = value;
            _semaphore = new SemaphoreSlim(1, 1);
        }

        internal int Capture()
        {
            return ++_captureCount;
        }

        internal int Release()
        {
            _captureCount--;
            _semaphore.Release();
            return _captureCount;
        }

        #endregion


        #region Public methods

        public T Value => _value;

        public async Task WaitAsync(CancellationToken cancellationToken)
        {
            await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
            cancellationToken.ThrowIfCancellationRequested();
        }

        #endregion


        #region IDisposable

        /// <inheritdoc />
        public void Dispose()
        {
            _synchronizer.Unlock(this);
        }

        #endregion
    }
24 авг 18, 15:03    [21653254]     Ответить | Цитировать Сообщить модератору
 Re: Асинхронное блокирование ресурса по значению  [new]
ЕвгенийВ
Member

Откуда: Москва
Сообщений: 4788
            SyncLock<T> syncLock;
            lock (_lock)
            {
                if (!_syncLocks.TryGetValue(value, out syncLock))
                {
                    syncLock = new SyncLock<T>(this, value);
                    _syncLocks.Add(value, syncLock);
                }
                syncLock.Capture();

https://docs.microsoft.com/ru-ru/dotnet/csharp/whats-new/csharp-7#out-variables
24 авг 18, 15:37    [21653298]     Ответить | Цитировать Сообщить модератору
 Re: Асинхронное блокирование ресурса по значению  [new]
ЕвгенийВ
Member

Откуда: Москва
Сообщений: 4788
hVostt,
Чего добиться то хочешь?
24 авг 18, 15:52    [21653307]     Ответить | Цитировать Сообщить модератору
Топик располагается на нескольких страницах: [1] 2   вперед  Ctrl      все
Все форумы / WinForms, .Net Framework Ответить