- Регистрация
- 06.11.2018
- Сообщения
- 11 789
- Благодарностей
- 5 726
- Баллы
- 113
Удивительно, но поиск по тэгу ReaderWriterLockSlim, по форуму зенки не нашел ни одного поста с применением этого функционала. Надо закрыть этот пробел
Итак подробно про ReaderWriterLockSlim читаем тут
В вкратце суть, обычный лок создает исключительную блокировку кода, а если работаем через глобальную переменную, то любой шаблон который использует эту блокировку будет простаивать в этом месте.
А так как есть операции с ресурсами которые занимают сотни миллисикунд, то уже сотня потоков запросто уходит в паузу на десятки секунд.
В определенных ситуациях это необходимые жертвы, например запись в файл, так как без блокировки у нас получится битый/невалидный набор данных в конкурирующем ресурсе.
Однако очень много ситуаций когда надо просто прочитать данные из объекта, что не является критической ситуацией для работы в многопотоке и читать безопасно могут сразу сотни потоков. И в этой ситуации нам надо иметь блокировку которая не будет блокировать чтение из ресурса, но при попытке записи в разделяемый ресурс она должна сработать как исключительная и выдать разрешение только одному потоку.
И вот класс ReaderWriterLockSlim как раз все это реализует. У него даже есть режим апгрейда блокировки чтения в блокировку записи прямо на лету, что очень удобно когда поиск в разделяемом ресурсе может затянуться и что бы не блокировать работу всех потоков на время поиска, сначала ставиться блокировка на чтение, а когда наступает время внести изменения в объект, то она меняется на режим записи и после выполнения операции сразу же меняется на чтение.
Все это можно посмотреть в примере по ссылке.
Теперь применим это к зенно. В примерах сделано для отдельного приложения, в котором есть общие статичные объекты, а в зенно то у нас разные шаблоны и у них нет общих статиков.
Тут нам на помощь придут глобальные переменные.
Итак, первым делом инициализируем глобальную переменную с нашим Не исключительным локом.
Теперь в месте, где код только читает и не вносит изменение в разделяемый ресурс используем следующие команды
в результате у нас строки 8,9,10 окажутся в локе Чтения. Все потоки с этим локом будут выполняться одновременно до тех пор пока у какого ни будь потока не сработает следующий код
Как только выполнение кода войдет в лок Записи, то он получает исключительные права на однопоточное выполнение и все локеры чтения будут заблокированы на вход и будет инициализирована пауза до момента выхода всех потоков из лока Чтения.
Как только лок Записи выполнится, то сразу же все локеры Чтения получат право зайти в этот лок.
По апгреду лока, смотрите пример по ссылке, там просто. используются EnterUpgradeableReadLock и ExitUpgradeableReadLock
Еще хочу сделать замечание, что это я привел простенький пример, он немного не подходит для копипаста в рабочие проекты, так как тут есть опасность невыполнения команды выхода из лока, особенно чревато при работе с файлами и их непредвиденными ошибками. Поэтому надо обязательно применять конструкцию try{}cath{}finale{} и выход из лока помещать в finale
примерно вот так.
Пару тестовых шаблонов приложил, так сказать для наглядного закрепления материала 

Итак подробно про ReaderWriterLockSlim читаем тут
В вкратце суть, обычный лок создает исключительную блокировку кода, а если работаем через глобальную переменную, то любой шаблон который использует эту блокировку будет простаивать в этом месте.
А так как есть операции с ресурсами которые занимают сотни миллисикунд, то уже сотня потоков запросто уходит в паузу на десятки секунд.
В определенных ситуациях это необходимые жертвы, например запись в файл, так как без блокировки у нас получится битый/невалидный набор данных в конкурирующем ресурсе.
Однако очень много ситуаций когда надо просто прочитать данные из объекта, что не является критической ситуацией для работы в многопотоке и читать безопасно могут сразу сотни потоков. И в этой ситуации нам надо иметь блокировку которая не будет блокировать чтение из ресурса, но при попытке записи в разделяемый ресурс она должна сработать как исключительная и выдать разрешение только одному потоку.
И вот класс ReaderWriterLockSlim как раз все это реализует. У него даже есть режим апгрейда блокировки чтения в блокировку записи прямо на лету, что очень удобно когда поиск в разделяемом ресурсе может затянуться и что бы не блокировать работу всех потоков на время поиска, сначала ставиться блокировка на чтение, а когда наступает время внести изменения в объект, то она меняется на режим записи и после выполнения операции сразу же меняется на чтение.
Все это можно посмотреть в примере по ссылке.
Теперь применим это к зенно. В примерах сделано для отдельного приложения, в котором есть общие статичные объекты, а в зенно то у нас разные шаблоны и у них нет общих статиков.
Тут нам на помощь придут глобальные переменные.
Итак, первым делом инициализируем глобальную переменную с нашим Не исключительным локом.
Инициализация локера:
// Создание объектов синхронизации в глобальной переменной для изменения списков,
// таблиц, других глобальных переменных и т.д. в разных шаблонах
// неймспейс и имя новой переменой для синхронизации
string namespaceName = "test_namespace";
string globVarName = "my_sync_object2";
// Выполняется в начале проекта
lock (project.GlobalVariables)
{
// проверка на существование глобальной переменной
try {
// попытка получения объекта (переход на catch при неудаче)
var syncobj = project.GlobalVariables[namespaceName, globVarName];
return syncobj.ToString(); // возврат его значения (отобразится в логе PM, если возможно)
} catch (KeyNotFoundException ex) {
// создание объекта синхронизации и его установка
object syncobj = new ReaderWriterLockSlim();
project.GlobalVariables.SetVariable(namespaceName, globVarName, syncobj);
}
}
Лок на чтение:
ReaderWriterLockSlim syncObject = (ReaderWriterLockSlim) project.GlobalVariables["test_namespace", "my_sync_object2"].Value;
for (int i = 0; i <= 50; i++)
{
project.SendInfoToLog("1 - Пауза, перед локом", true);
Thread.Sleep(300);
syncObject.EnterReadLock();
project.SendInfoToLog("1 - Заход в лок", true);
Thread.Sleep(300);
project.SendInfoToLog("1 - Выход из лока", true);
syncObject.ExitReadLock();
}
Лок на запись:
ReaderWriterLockSlim syncObject = (ReaderWriterLockSlim) project.GlobalVariables["test_namespace", "my_sync_object2"].Value;
for (int i = 0; i <= 50; i++)
{
project.SendInfoToLog("2 - Пауза, перед локом", true);
Thread.Sleep(5000);
syncObject.EnterWriteLock();
project.SendInfoToLog("2 - Заход в лок", true);
Thread.Sleep(5000);
project.SendInfoToLog("2 - Выход из лока", true);
syncObject.ExitWriteLock();
}
Как только лок Записи выполнится, то сразу же все локеры Чтения получат право зайти в этот лок.
По апгреду лока, смотрите пример по ссылке, там просто. используются EnterUpgradeableReadLock и ExitUpgradeableReadLock
Еще хочу сделать замечание, что это я привел простенький пример, он немного не подходит для копипаста в рабочие проекты, так как тут есть опасность невыполнения команды выхода из лока, особенно чревато при работе с файлами и их непредвиденными ошибками. Поэтому надо обязательно применять конструкцию try{}cath{}finale{} и выход из лока помещать в finale
примерно вот так.
Безопасная конструкция:
ReaderWriterLockSlim syncObject = (ReaderWriterLockSlim) project.GlobalVariables["test_namespace", "my_sync_object2"].Value;
try
{
syncObject.EnterWriteLock();
// тута индуский код
}
catch
{
// тута алярмы после индуского кода
}
finally
{
syncObject.ExitWriteLock();
}

Вложения
-
11,6 КБ Просмотры: 110
-
11,6 КБ Просмотры: 105
Для запуска проектов требуется программа ZennoPoster.
Это основное приложение, предназначенное для выполнения автоматизированных шаблонов действий (ботов).
Подробнее...
Для того чтобы запустить шаблон, откройте программу ZennoPoster. Нажмите кнопку «Добавить», и выберите файл проекта, который хотите запустить.
Подробнее о том, где и как выполняется проект.