Особенности использования стандартных lock'ов для многопотока

Yuriy Zymlex

Moderator
Команда форума
Регистрация
24.10.2016
Сообщения
6 529
Благодарностей
3 375
Баллы
113
Так и пишут в библиотеке, что SyncObjects.ListSyncer является устаревшим, используйте FileSyncObjects.ListSyncer.
Вы можете и дальше использовать "устаревшие" поля, удалять их не будут.
Новый FileSyncObjects.ListSyncer тоже блокирует все шаблоны?
Как помню, да + обладает дополнительной логикой для установки приоритетов.
Уточню по примерам FileSyncObjects у разработчиков.
 
  • Спасибо
Реакции: ZSharp и Mikhail B.

Yuriy Zymlex

Moderator
Команда форума
Регистрация
24.10.2016
Сообщения
6 529
Благодарностей
3 375
Баллы
113
Может админы или модераторы прокомментируют?
Данный способ чуть менее производительный, но даёт чуть больше контроля:
C#:
using (FileSyncObjects.ListSyncer.Lock())
{
    // код со списками
}
using (FileSyncObjects.TableSyncer.Lock())
{
    // код с таблицами
}
using (FileSyncObjects.GoogleSpreadsheetsSyncer.Lock())
{
    // код с гугл таблицами
}
доступны Lock и HighLock, они требуют вызова Dispose, поэтому используется using.
HighLock имеет приоритет в очереди.
Так же доступны варианты для асинхронных методов - LockAsync и HighLockAsync.
 

ZSharp

Client
Регистрация
29.09.2013
Сообщения
396
Благодарностей
126
Баллы
43

Mikhail B.

Client
Регистрация
23.12.2014
Сообщения
14 438
Благодарностей
5 459
Баллы
113

Yuriy Zymlex

Moderator
Команда форума
Регистрация
24.10.2016
Сообщения
6 529
Благодарностей
3 375
Баллы
113
Так всё-таки лочат все шаблоны?
Да, действует на весь постер, там где используется конкретный *Syncer.
Про это можно по подробнее?
Синхронные локи опираются на ID потока, в асинхронном выполнении поток может меняться и лок может работать неправильно.
 
Последнее редактирование:
  • Спасибо
Реакции: ZSharp

timoxa949

Client
Регистрация
16.11.2018
Сообщения
158
Благодарностей
27
Баллы
28
Данный способ чуть менее производительный, но даёт чуть больше контроля:
C#:
using (FileSyncObjects.ListSyncer.Lock())
{
//код со списками
}
using (FileSyncObjects.TableSyncer.Lock())
{
//код с таблицами
}
using (FileSyncObjects.GoogleSpreadsheetsSyncer.Lock())
{
//код с гугл таблицами
}
доступны Lock и HighLock, они требуют вызова Dispose, поэтому используется using.
HighLock имеет приоритет в очереди.
Так же доступны варианты для асинхронных методов - LockAsync и HighLockAsync.
Подскажите пожалуйста, как внести списки и таблицы в этот код? Что писать? Спасибо.
 

Denchen

Client
Регистрация
23.06.2020
Сообщения
22
Благодарностей
3
Баллы
3
Спасибо большое за код, подскажите новичку правильно ли понял?:
- Если в общем коде прописать:

//Объект для первого списка
"public static object ListSyncer1 = new object();
//Объект для второго списка"
"public static object ListSyncer2 = new object();"

при этом в первом шаблоне прописать:

//Лочим код изменения списка для многопотока
lock (CommonCode.ListSyncer1){
//Добавляем в список "Список 1" элемент со значением "строка"
project.Lists["Список 1"].Add("строка");
}

а во втором шаблоне прописать:

//Лочим код изменения списка для многопотока
lock (CommonCode.ListSyncer2){
//Добавляем в список "Список 2" элемент со значением "строка"
project.Lists["Список 2"].Add("строка");
}

то каждый шаблон будет ходить через свой турникет?
 

GeGold

Client
Регистрация
28.09.2015
Сообщения
785
Благодарностей
373
Баллы
63
Доброго времени суток , попробовал коды выше -не получилось

имеется вот такой вод код , он вставляет из переменной текст

C#:
var textcr = project.Variables["mail"].Value;
System.Windows.Forms.Clipboard.SetText(textcr);
instance.ActiveTab.KeyEvent("v","press","ctrl");//вставить текст из переменной
Из кода понятно , что ничего не лочится и при работе более одного потока буфер будет одним, в силу "опыта" работы с C# своих мозгов не хватает, подскажите что именно добавить в буфер, чтобы можно было запустить шаблон во многопотоке

Желательно лочить на уровне потока
 

Valandersi

Client
Регистрация
19.01.2015
Сообщения
1 935
Благодарностей
1 123
Баллы
113
Кубик получить случайную строку и положить в переменную так же лочит список?
 
Регистрация
26.05.2020
Сообщения
514
Благодарностей
173
Баллы
43
Так что в итоге? Какой лок работать должен и нужен ли он сейчас, спустя 4 года от создания темы?

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

Yuriy Zymlex

Moderator
Команда форума
Регистрация
24.10.2016
Сообщения
6 529
Благодарностей
3 375
Баллы
113
Так что в итоге? Какой лок работать должен и нужен ли он сейчас, спустя 4 года от создания темы?

Вопрос возник, ибо есть шаблон, в котором несколько таблиц (2 на чтение строк и 2 на запись). Решил его перевести на многопоток. Вроде все правильно сделал (браузерный шаб на кубиках - чистый хардкор), а вот за неоднократно обсуждаемый лок очкую его даже запускать...
Данная статья касается только C#.
Блокировка в C# необходима, когда несколько действий в нём не должны выполняться одновременно.

Простые экшены сами по себе потокобезопасны, блоки экшенов - нет.
 
  • Спасибо
Реакции: Андрейка2020
Регистрация
26.05.2020
Сообщения
514
Благодарностей
173
Баллы
43
Простые экшены сами по себе потокобезопасны, блоки экшенов - нет.
То есть, если шаб на кубиках, то можно не переживать? А блоки экшенов - это которые "Свой C#-код"?
 

Yuriy Zymlex

Moderator
Команда форума
Регистрация
24.10.2016
Сообщения
6 529
Благодарностей
3 375
Баллы
113
То есть, если шаб на кубиках, то можно не переживать?
Да, экшены записи не будут выполнены одновременно и строки запишутся правильно (а не одна в другой).
А блоки экшенов - это которые "Свой C#-код"?
Нет, это когда вы объединяете экшены в блок (в том числе и C# экшены).
 
Последнее редактирование:
Регистрация
26.05.2020
Сообщения
514
Благодарностей
173
Баллы
43
Нет, это когда вы объединяете экшены в блок (в том числе и C# экшены).
Лог в зенке

71512


В мейкере все рОвно отрабатывает при пошаговой отработке... Беру из списка юзеров их количество, дабы обращаться к ним потом в потоке по номеру строки (ибо удаление строк и последующее добавление также срабатывает с лагами...)

71513


Полагаю, трабла именно в отсутствии локов. С привязкой к файлу тоже пробовал и там тоже баги... Что я делаю неправильно здесь?
 

Yuriy Zymlex

Moderator
Команда форума
Регистрация
24.10.2016
Сообщения
6 529
Благодарностей
3 375
Баллы
113
Полагаю, трабла именно в отсутствии локов. С привязкой к файлу тоже пробовал и там тоже баги... Что я делаю неправильно здесь?
Если у вас первый и четвёртый экшен работает с одним списком, то вам необходимо переходить на C#, что бы объединить эти действия под один lock.
 

volody00

Client
Регистрация
06.09.2016
Сообщения
928
Благодарностей
1 014
Баллы
93

Вложения

  • 13,4 КБ Просмотры: 166
  • 69 байт Просмотры: 173
Регистрация
26.05.2020
Сообщения
514
Благодарностей
173
Баллы
43
Если у вас первый и четвёртый экшен работает с одним списком, то вам необходимо переходить на C#, что бы объединить эти действия под один lock.
Вроде победил...

71522


71524


А вот кубики сами

71523
 

bigloafer

Client
Регистрация
23.07.2020
Сообщения
243
Благодарностей
76
Баллы
28

Yuriy Zymlex

Moderator
Команда форума
Регистрация
24.10.2016
Сообщения
6 529
Благодарностей
3 375
Баллы
113
А где найти в документации данное, про FileSyncObjects.ListSyncer и SyncObjects.ListSyncer?
Сейчас вы можете посмотреть только через подсказки PM, либо стороннюю IDE - это та же декомпиляция, там будет видна реализация (с документацией) публичных и открытых частей библиотек.
Создал таску, что бы в будущем не забыли добавить в C# wiki.
 
Последнее редактирование:

Metrix

Client
Регистрация
03.01.2014
Сообщения
342
Благодарностей
272
Баллы
63
100500 объектов синхронизации тоже так себе идея, ибо при таком количестве в больших шаблонах есть опасность залочить ресурс не тем объектом и "привет, баг!", который будет тяжело отловить.

Проще лочить ресурс в Зенке им же. Подчеркиваю, именно в Зенке.

Если запустите этот код, например, в 10 потоков, то в список запишется 10000 строк и ни одна не пропадет.
Код:
var list1 = project.Lists["Список 1"];

for(int i = 0; i < 1000; i++)
{
    lock(list1)
    {
        list1.Add("Поток " + System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());
    }
}
Некропостинг, но как сам выяснил относительно недавно и не получил пока внятного ответа на этот вопрос, с таким локом, приведённым выше получаем и дубли и пересечения между потоками.
Такой лок НЕпотокобезопасен !!!
C#:
var startList = project.Lists["startList"];
lock(startList)
{
    //Some code
}
Так что, придётся использовать 100500 объектов синхронизации.
Прикладываю тестовый шаблон. После прогона нескольких десятков тысяч выполнений в 1000 потоков, к примеру, в конечном списке получаем дубли. Для сравнения, кубик ниже со специально созданными статическими объектами блокировки, там дублей в конечном списке нет. Ну и, естественно, в стартовом списке дубли отсутствуют, не верьте мне на слово, проверьте сами!
 

Вложения

  • Спасибо
Реакции: LaGir, Roman48 и volody00

Phoenix78

Client
Read only
Регистрация
06.11.2018
Сообщения
11 789
Благодарностей
5 721
Баллы
113
Некропостинг, но как сам выяснил относительно недавно и не получил пока внятного ответа на этот вопрос, с таким локом, приведённым выше получаем и дубли и пересечения между потоками.
Такой лок НЕпотокобезопасен !!!
C#:
var startList = project.Lists["startList"];
lock(startList)
{
    //Some code
}
Так что, придётся использовать 100500 объектов синхронизации.
Прикладываю тестовый шаблон. После прогона нескольких десятков тысяч выполнений в 1000 потоков, к примеру, в конечном списке получаем дубли. Для сравнения, кубик ниже со специально созданными статическими объектами блокировки, там дублей в конечном списке нет. Ну и, естественно, в стартовом списке дубли отсутствуют, не верьте мне на слово, проверьте сами!
Я конечно не гуру С#, но мне кажется лочить локальную переменную не самая лучшая идея для работы в многопотоке. ;-)
Во всех примерах используются общие объекты, объявленные в общем коде или в общем классе зенки.... А у тебя в примере непойми что и сбоку бантик ;-)
 

Roman48

Client
Регистрация
28.02.2016
Сообщения
2 058
Благодарностей
746
Баллы
113
Все понятно, автор молодец!
Все же есть пару уточнений, старт шаблона, я привязал список и сделал лок списка №1, шаблон выполняется минут 15 (примерно) работает еще со списками, назовем их список №2, список№3, они тоже лакаются, проходит еще минут 10, шаблон дошел до самого конца, и надо добавить в список №1 строку, перед добавлением в список №1 надо его еще раз делать лок, ведь я уже делал лок в начале шаблона?
Но и другие потоки, тоже делали лок, списку №1 и другим спискам.
 

Phoenix78

Client
Read only
Регистрация
06.11.2018
Сообщения
11 789
Благодарностей
5 721
Баллы
113
Все понятно, автор молодец!
Все же есть пару уточнений, старт шаблона, я привязал список и сделал лок списка №1, шаблон выполняется минут 15 (примерно) работает еще со списками, назовем их список №2, список№3, они тоже лакаются, проходит еще минут 10, шаблон дошел до самого конца, и надо добавить в список №1 строку, перед добавлением в список №1 надо его еще раз делать лок, ведь я уже делал лок в начале шаблона?
Но и другие потоки, тоже делали лок, списку №1 и другим спискам.
Все просто. Лок не вечен. Ты просто не правильно понимаешь как он работает. Ты делаешь Лок только в одном кубике и только в одном конкретном месте , это все что между {} , как только код вышел из этих скобок, Лок перестает действовать. Или я просто не уловил, что у тебя весь шаблон в одном кубике С# и Лок действует все 15 минут ?
 
  • Спасибо
Реакции: Roman48

Roman48

Client
Регистрация
28.02.2016
Сообщения
2 058
Благодарностей
746
Баллы
113
Все просто. Лок не вечен. Ты просто не правильно понимаешь как он работает. Ты делаешь Лок только в одном кубике и только в одном конкретном месте , это все что между {} , как только код вышел из этих скобок, Лок перестает действовать. Или я просто не уловил, что у тебя весь шаблон в одном кубике С# и Лок действует все 15 минут ?
Ясность полная.

Можешь зайти
 

bigloafer

Client
Регистрация
23.07.2020
Сообщения
243
Благодарностей
76
Баллы
28
Ясность полная.
Лучше всего обработать пакет данных, используя временные листы - уровня кубика (не уровня проекта).
А в lock -е просто скопировать пакет данных из временного листа в лист проекта.
Так будет работать быстрее и другие потоки простаивать будут меньше.
А то многие в lock-и загоняют чуть ли не всю основную логику.
 
  • Спасибо
Реакции: cmvvo и Roman48

Metrix

Client
Регистрация
03.01.2014
Сообщения
342
Благодарностей
272
Баллы
63
Я конечно не гуру С#, но мне кажется лочить локальную переменную не самая лучшая идея для работы в многопотоке. ;-)
Во всех примерах используются общие объекты, объявленные в общем коде или в общем классе зенки.... А у тебя в примере непойми что и сбоку бантик ;-)
Т.е. ты не разбираешься, но решил выразить своё авторитетное мнение? Молодец, что тут скажешь :-) :ay:
Чем тебе не нравится лок локальной переменной? Если посмотреть внимательно для чего она, то всё становится понятно. Если мы будем определять индекс строки, с которой будем работать вне лока, то однажды получим исключение "Индекс находился за пределами диапазона", потому что другой поток уже может удалить строку с этим индексом (например, она была последней). А то, что объявление переменной внутри лока, это ничего не меняет существенно.
Ты понимаешь смысл блокировки общими объектами? Если ты про SyncObjects.ListSyncer, то это общий объект для всех списков всех шаблонов, соответственно, общая очередь. Если ты про специально созданные статические объекты для блокировки public static object startListLocker = new object(); так в проекте есть кубик ниже того, что со стрелкой. Минусы подхода с блокировкой специально созданными локерами тоже есть: можно запутаться при наличии большого количества списков в шаблоне, если методы работы со списками вынесены в общий код, то дополнительно придётся прокидывать для работы с каждым списком свой локер, что также может вызвать путаницу.
По поводу бантика, посмотри внимательно пост, который процитирован и подумай ещё раз.
Доступ к объекту можно блокировать этим же самым объектом, в случае со списками/таблицами Зеннопостера это не так.
Все понятно, автор молодец!
Все же есть пару уточнений, старт шаблона, я привязал список и сделал лок списка №1, шаблон выполняется минут 15 (примерно) работает еще со списками, назовем их список №2, список№3, они тоже лакаются, проходит еще минут 10, шаблон дошел до самого конца, и надо добавить в список №1 строку, перед добавлением в список №1 надо его еще раз делать лок, ведь я уже делал лок в начале шаблона?
Но и другие потоки, тоже делали лок, списку №1 и другим спискам.
Лок нужно стремиться делать максимально коротким по времени, для совершения манипуляций со списком, поэтому, например, если нам надо взять строку с удалением из списка и потом распарсить её, то лочим мы только взятие и удаление, а парсим за пределами лока. Если же нам надо сделать изменения в части строки, то нам нужно взять строку, определить её индекс, удалить строку, сделать изменения в строке и вставить уже эту обновлённую строку в список по этому же индексу, всё это нужно будет проделать в пределах одного лока
 
Последнее редактирование:
  • Спасибо
Реакции: material и Roman48

ZSharp

Client
Регистрация
29.09.2013
Сообщения
396
Благодарностей
126
Баллы
43
Так что, придётся использовать 100500 объектов синхронизации.
Всё правильно.
Нужно использовать статический неизменяемый локер для каждого отдельного объекта (если он нужен).
Если 10 списков, то нужно заводить 10 статических локеров, и никак не использовать сам список в качестве локера.

 

Roman48

Client
Регистрация
28.02.2016
Сообщения
2 058
Благодарностей
746
Баллы
113
Временные файлы, лочить не надо же, про временные я имею в виду, файл в внутри шаблона?
1613391083050.png
Такой список, у каждого потока свой список?
 

ZSharp

Client
Регистрация
29.09.2013
Сообщения
396
Благодарностей
126
Баллы
43
можно запутаться при наличии большого количества списков в шаблоне, если методы работы со списками вынесены в общий код, то дополнительно придётся прокидывать для работы с каждым списком свой локер, что также может вызвать путаницу.
чтобы не путаться добавляйте имя объекта к локу.
Допустим есть у вас список startList, то добавьте лок с именем
C#:
public static object LockStartList = new object();
или
C#:
public static object StartListLock = new object();
И не будете путаться.
Или вообще можно сделать отдельный класс локеров, а локеры называть именами объектов
C#:
    public class Locker
    {
        public static object startList = new object();
        public static object endList = new object();
        public static object secondList = new object();
        public static object secondTable = new object();
    }
А вызывать вот так
C#:
lock(Locker.startList)
{
     startList.Add("");
}
 

ZSharp

Client
Регистрация
29.09.2013
Сообщения
396
Благодарностей
126
Баллы
43
Временные файлы, лочить не надо же, про временные я имею в виду, файл в внутри шаблона?
если не важно изменение, то не надо.
Лочить, только если важно изменение данных.
Допустим вы регистрируете аккаунты, и у вас в файле лежат доступы к почтам.
Список
email1:pass1
email2:pass2
email3:pass3
email4:pass4
email5:pass5

Вам нельзя чтобы несколько потоков начали регистрировать аккаунты с одним и тем же email-ом, вот тогда нужно блокировать,
чтобы каждый поток отработал полный цикл
Цикл у нас, берём первую строку, и удаляем её после взятия.

1-й поток
1) заблокировал файл,
2) взял email, email1:pass1
3) удалил его из списка, email1:pass1
4) и снял блокировку чтобы другой поток мог сделать тоже самое.

2-й поток
1) заблокировал файл,
2) взял email, email2:pass2
3) удалил его из списка, email2:pass2
4) и снял блокировку чтобы другой поток мог сделать тоже самое.

3-й поток
1) заблокировал файл,
2) взял email, email3:pass3
3) удалил его из списка, email3:pass3
4) и снял блокировку чтобы другой поток мог сделать тоже самое.


А если не ставить блокировку, то будет вот так приблизительно:
1-й поток
взял email, email1:pass1

в этот момент
2-й поток
взял email, email1:pass1

в этот момент
3-й поток
взял email, email1:pass1

Затем в коде идёт удаление email-a (певой строки)
1-й поток
удалил его из списка, email1:pass1

в этот момент
2-й поток
удалил его из списка, email2:pass2

в этот момент
3-й поток
удалил его из списка, email3:pass3



И в итоге у вас 3 потока с одним и тем же ящиком: email1:pass1
А ящики
email2:pass2
email3:pass3

совсем не использовались, но были удалены из списка

И из этих 3-х потоков зарегистрирует аккаунт только какой-то один, кто первый успеет.
 
  • Спасибо
Реакции: mr.green и Roman48

Кто просматривает тему: (Всего: 1, Пользователи: 0, Гости: 1)