Mutex для Zennoposter. Как обезопасить любые критические действия в многопотоке.

LexxWork

Client
Регистрация
31.10.2013
Сообщения
1 190
Благодарностей
785
Баллы
113
Привет всем. В этой статье я расскажу о возможности защиты критически важных моментов любого шаблона при работе в многопоточном режиме.
О чем это он? Что это значит? Если на ум приходят такие вопросы, значит вам это либо не нужно либо еще рано.
Баблорубам - даже не заглядывайте сюда.
Данная информация рассчитана на более искушенного пользователя программы а так же для тех, кому действительно интересно узнать что-то реально новое, уникальное, необычное.

Предыстория.
Все пользователи программы рано или поздно сталкиваются с удивительными непонятными ошибками во время работы шаблонов в многопоточном режиме. То файл не так пишется, данные какие-то кривые, ошибки какие-то странные вылазят. Постоянно на форуме устраивают разбор полетов по поводу многопотока.
Логическим завершением таких полетов становится понимание (либо непонимание) того как нужно поступать чтобы не было конфликтов в совместно используемых ресурсах.
Я тут еще немножко нудотины попишу - перечислю проблемные места:
-файлы. Зеннопостер предлагает несколько вариантов. Это список, таблица и просто файл.
-эмуляторы. Можно спокойно пользоваться мышкой и клавой.
Все они, судить не мне, работают в многопотоке достаточно стабильно.
И все. На этом проблемные места заканчиваются, но так ли это?

Проблемное место.
Когда-то давно я писал для себя собственный эмулятор мыши и не думал делать свою библиотеку потоконезависимой. Но чем больше я рассказывал о ней, тем больше слышал - "а она работает в многопотоке?". Вот же!..
Вот собственно из-за этого случайным образом возникла идея реализовать что-то похожее - лишь бы работало.
Сразу говорю, что реализовал идею с многопотоком для своей мыши достаточно успешно, на форуме даже видео выложил.
Оказалось вот что: техника, которую я применил к своей мышке, применима ко всему.

Совсем недавно, задавал вопрос по поводу достаточно ли вам, дорогие читатели, многопоточности только в файлах и эмуляции?
Если да, то хорошо, а если нет? Допустим вы работаете с файлом напрямую. Ну например потому что он очень большой и захламлять оперативку вам не охота.
Остановимся на этом случае.
Какой результат нас будет ожидать если мы запустим такой шаблон в несколько потоков? Повезет - не конфликтуют, не повезет - все полетит к чертям.

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

mutex.PNG


Как работать с библиотекой.
Вам нужно реализовать класс Z.ZMutex для последующей блокировки кода и уничтожить его когда проблемное место закончилось.
Конструктор класса ZMutex(string GlobalMutexID, int timeOut) принимает два аргумента:
GlobalMutexID - уникальное имя блокировки, например "ANY_UNIQUE_NAME_FOR_GLOBAL_MUTEX__1234567890"
timeOut - тайм аут ожидания освобождения ресурса. Если меньше 0, мьютекс будет ждать до бесконечности пока не освободится ресурс.
Если timeOut = N > 0, то по истечении времени ZMutex выдаст исключение о том что он ждал, но не дождался.

Продемонстрирую простейший пример в С# сниппете:

using (var mymutex = new Z.ZMutex("ANY_UNIQUE_NAME_FOR_GLOBAL_MUTEX__1234567890", -1))
{
//здесь и будет весь ваш небезопасный код
System.IO.File.AppendAllText(project.Directory+"\\test.txt", "asdfasdfasdfasdf23r2r2rqwfwoifjof"+Environment.NewLine, Encoding.UTF8-);
}


В шаблоне mutex_test.xmlz показан пример работы выходя за рамки сниппета. Фактически вы можете использовать любые макросы и любую логику, но которая в конечном итоге ведет к освобождению ресурса.
Перед тяжелым кодом мы создаем свой код:

string GlobalMutexID = "ANY_UNIQUE_NAME_FOR_GLOBAL_MUTEX__1234567890";
int timeOut = -1; //-1 - wait infinitely; if timeOut is > 0, waits timeOut ms and than raise TimeoutException
project.Context["mutex"] = new Z.ZMutex(GlobalMutexID, timeOut);


а по окончании

var mutex = (Z.ZMutex)project.Context["mutex"];
mutex.Dispose();


Как правильно понимать действие блокировщика.
Данный метод работает не на уровне одного процесса - он работает глобально для всей ОС. Это очень важно.
От сюда следует, что уникальное имя мьютекса не должно использоваться в других потоках или процессах которые блокируют совершенно другой ресурс или вообще не участвуют в жизни ресурса.
Мьютекс не блокирует сам ресурс, а только лишь дальнейшее продолжение программы.
От сюда вывод: если вы где либо блокируете ресурс, то блокировки нужно ставить везде, где данный ресурс используется одновременно.

Вот и все.
Пригодится - хорошо, нет - тоже хорошо.
 
Тема статьи
Нестандартные хаки
Номер конкурса статей
Второй конкурс статей

Вложения

Для запуска проектов требуется программа ZennoPoster или ZennoDroid.
Это основное приложение, предназначенное для выполнения автоматизированных шаблонов действий (ботов).
Подробнее...

Для того чтобы запустить шаблон, откройте нужную программу. Нажмите кнопку «Добавить», и выберите файл проекта, который хотите запустить.
Подробнее о том, где и как выполняется проект.

Последнее редактирование модератором:

Radzhab

Client
Регистрация
23.05.2014
Сообщения
1 500
Благодарностей
1 263
Баллы
113
Я думаю такой функционал надо пихать в зеннку в виде куба.
 

lokiys

Moderator
Регистрация
01.02.2012
Сообщения
4 768
Благодарностей
1 179
Баллы
113

darkdiver

Administrator
Команда форума
Регистрация
13.01.2009
Сообщения
2 284
Благодарностей
2 728
Баллы
113
В BadEnd надо обязательно запихивать вызов
C#:
var mutex = (Z.ZMutex)project.Context["mutex"];
mutex.Dispose();
Иначе может случить ситуация, что проект упадет ДО вызова разблокировки mutex
 
  • Спасибо
Реакции: Yuriy Zymlex

LexxWork

Client
Регистрация
31.10.2013
Сообщения
1 190
Благодарностей
785
Баллы
113
спасибо darkdiver за замечание и спасибо что добавили в конкурс.
Добавлю лишь что BadEnd макрос не знает произошло ли освобождение ресурсов. Поэтом для каждого мутекса нужно проделывать следующее.
var mutex = (Z.ZMutex)project.Context["mutex"];
if(mutex != null) mutex.Dispose();
Не знаю насчет контекста, возращает ли он null если обьекта нет, во всяком слчае все равно нужно как-то проверять.

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

Gfoblin

Client
Регистрация
30.05.2013
Сообщения
4 510
Благодарностей
990
Баллы
113
Круто, но я решил это в 3 кубика через глобальные )
 

LexxWork

Client
Регистрация
31.10.2013
Сообщения
1 190
Благодарностей
785
Баллы
113
изначально я вписал мутекс в свою либу, а там нет зависимостей от зенки.
какое ожидание ставить, если ресурс блокируется на милисекунды?...
 

kyvour

Активный пользователь
Регистрация
17.05.2014
Сообщения
186
Благодарностей
36
Баллы
28
А можно по подробнее о том что все-таки мютекс делает? Как я понял:
У нас есть шаб с мютексом, работающий в 2 потока.
1-й поток подходит, например, к участку где идет запись в файл. Здесь создается новый мютекс, который блокирует файл или не файл все же? И 2-й поток дойдя до этого места будет ждать пока снимется блокировка или какое-то максимальное время?
 

kyvour

Активный пользователь
Регистрация
17.05.2014
Сообщения
186
Благодарностей
36
Баллы
28
Что будет с данными, если 2-й поток прождал, например, 30 секунд а блокировка так и не снялась. Подозреваю, что будет потеря данных из 2-го потока.
 

kyvour

Активный пользователь
Регистрация
17.05.2014
Сообщения
186
Благодарностей
36
Баллы
28
Но так как у вас написано, что по истечении тайм аута мютекс выдает исключение - выходит, что это самое исключение можно выловить шабом и остановить проект, чтобы не терялись данные
 

LexxWork

Client
Регистрация
31.10.2013
Сообщения
1 190
Благодарностей
785
Баллы
113
мутекс не блокирует сам файл или любой другой ресурс, а только остальные процессы с таким же мутексом.
Если любой другой процесс обратится без мутекса, то в худшем случае будет конфликт, если он предусмотрен на более низком уровне.
с таймаутом все верно.
 

kyvour

Активный пользователь
Регистрация
17.05.2014
Сообщения
186
Благодарностей
36
Баллы
28
Спасибо. Тогда возникает вопрос о глобальном ID мутекса - он должен быть одинаковым или унакальным в пределах потока/шаблона/системы?
 

LexxWork

Client
Регистрация
31.10.2013
Сообщения
1 190
Благодарностей
785
Баллы
113
должен быть уникальным в пределах системы. Любой процесс или поток с одинаковым мутексом будут работать слаженно.
Если пишете свою либу и есть критический ресус - используйте класс прямо в либе.
 

kyvour

Активный пользователь
Регистрация
17.05.2014
Сообщения
186
Благодарностей
36
Баллы
28
Я просто над шабами для зенно задумываюсь. Почти кажен шаб читает с файла номер строки, увеличивает его на 1 и записывает обратно. Бывает что Несколько потоков успевают одно и тоже значение считать с файла.
Как я понимаю, чтобы потоки ждали в очереди чтобы считать/записать файл, то для каждого потока ИД мутекса должен быть уникальным. И надо для ИД генерировать какой-то UUID?
 

LexxWork

Client
Регистрация
31.10.2013
Сообщения
1 190
Благодарностей
785
Баллы
113
для каждого проекта ид должен быть уникальным. В пределах одного проека ид должен быть постоянным.
Может вам лучше и проще воспользоваться глобальными переменными? Я не знаю насколько это эффективно, но люди пользуются.
Насколько я представляю, там такая логика. Делаете уникальное имя окружения плюс уникальное имя для глобальной переменной. Она будет видна из всех шаблонов. Работаете с ней как с переключателем - 0\1. Если 0 - меняете значение на 1, выполняете там что вам нужно и возвращаете в 0. Если изначально значение 1, ждете пока не будет 0.
 
  • Спасибо
Реакции: Vik89

insideath

Client
Регистрация
18.05.2013
Сообщения
55
Благодарностей
25
Баллы
8
Весьма полезная статья, проголосовал! :ay:

Я думаю такой функционал надо пихать в зеннку в виде куба.
Точно! Куб, как простой способ ограничения потоков без всяких переменных. Жаль, что customactions не предусматриваются, приходится для нетривиальных задач лезть в обход.
 

LexxWork

Client
Регистрация
31.10.2013
Сообщения
1 190
Благодарностей
785
Баллы
113
спасибо большое, мне куда приятней знать, что это хоть кому-то нужно.

кстати, кто не знает, у каждого снипета есть свой SyncObject, который по сравнению с глобальными переменными работает куда лучше, но не на 100% В принципе если вам не нужно по 1000 раз в секунду перезаписывать файл, вполне достаточно и этого. Для других процессов растянутых во времени это ничем не грозит. Единственный минус лочится только снипет а не часть кода.
 
  • Спасибо
Реакции: Adigen

Vik89

Client
Регистрация
05.11.2014
Сообщения
71
Благодарностей
14
Баллы
8
...
Насколько я представляю, там такая логика. Делаете уникальное имя окружения плюс уникальное имя для глобальной переменной. Она будет видна из всех шаблонов. Работаете с ней как с переключателем - 0\1. Если 0 - меняете значение на 1, выполняете там что вам нужно и возвращаете в 0. Если изначально значение 1, ждете пока не будет 0.
в С# не разбираюсь, но за эту идею огромное спасибо. Внедрил у себя)
 

Leoneed

Client
Регистрация
03.05.2015
Сообщения
53
Благодарностей
38
Баллы
18
@LexxWork спасибо большое за решение всех проблем с многопотоком =)
Скомпилил либу под х64 - отлично работает.
Стояла задача запуска приложения в 1 экзмпляре в многопоточном проекте.
По логике: первый поток должен проверять запущено ли приложение, и если нет, производить запуск. Остальные потоки, в случае если приложение запущено - просто начинают работу.
Данное решение оказалось единственным работоспособным вариантом.
 

z@jivalo

Client
Регистрация
27.12.2016
Сообщения
798
Благодарностей
178
Баллы
43
Подскажите как собрать для x64?
Или если не трудно прикрепите готовый dll
 

Sz5

Client
Регистрация
10.12.2012
Сообщения
157
Благодарностей
186
Баллы
43
  • Спасибо
Реакции: z@jivalo

z@jivalo

Client
Регистрация
27.12.2016
Сообщения
798
Благодарностей
178
Баллы
43
А как добавить через GAC блок верно?

Видимо что-то я не то делаю ошибка


Код:
Desktop\mutex.dll не является .Net сборкой или не возможно получить доступ к сборке

System.NotSupportedException: An attempt was made to load an assembly from a network location which would have caused the assembly to be sandboxed in previous versions of the .NET Framework. This release of the .NET Framework does not enable CAS policy by default, so this load may be dangerous. If this load is not intended to sandbox the assembly, please enable the loadFromRemoteSources switch. See http://go.microsoft.com/fwlink/?LinkId=155569 for more information.
   at System.Reflection.RuntimeAssembly.nLoadFile(String path, Evidence evidence)
   at System.Reflection.Assembly.LoadFile(String path)
   at ZennoLab.DotNetResolver.DotNetResolver.AddExternalReference(String path)
   at ZennoLab.ProjectMaker.Controls.ProjectEditor.ProjectBar.StaticBlockSettings.GACReferences.H9sM7fbq3clMgl2x3sru(Object , Object )
   at ZennoLab.ProjectMaker.Controls.ProjectEditor.ProjectBar.StaticBlockSettings.GACReferences.fDCs3cknav(Object  , OkButtonClickArgs  )
 

linkod

Пользователь
Регистрация
11.10.2018
Сообщения
118
Благодарностей
1
Баллы
16
для каждого проекта ид должен быть уникальным. В пределах одного проека ид должен быть постоянным.
Может вам лучше и проще воспользоваться глобальными переменными? Я не знаю насколько это эффективно, но люди пользуются.
Насколько я представляю, там такая логика. Делаете уникальное имя окружения плюс уникальное имя для глобальной переменной. Она будет видна из всех шаблонов. Работаете с ней как с переключателем - 0\1. Если 0 - меняете значение на 1, выполняете там что вам нужно и возвращаете в 0. Если изначально значение 1, ждете пока не будет 0.
эммм. а чем сниппет от C# кода в кубике отличается? или если это одно и тоже, то получается, что лочится все выполнение кода?
 

Mikhail B.

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

linkod

Пользователь
Регистрация
11.10.2018
Сообщения
118
Благодарностей
1
Баллы
16

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