Delphi. Потоки. Мьютексы

Данная статья посвящена мьютексам и основана на личных экспериментах и материалах сети.

Что такое мьютексы?

Мьютекс это объект синхронизации потоков разных процессов, скажем 2-х копий одной и той же программы. Он очень похож на критические секции, но разница в том, что мьютекс обеспечивает межпроцессное взаимодействие. Например, если 2-м программам нужно писать в один файл, то как организовать корректное взаимодействие ведь каждая из программ это отдельный процесс? Как одной программе, заметьте программе, а не потоку сказать, чтобы ждала другую? Вот для этого и предназначены мьютексы. В итоге конечно используются потоки, но потоки разных процессов – в этом основное отличие от критических секций.

Одному процессу соответствует несколько потоков – посмотрите на рисунок ниже. Процессов гораздо меньше чем потоков.

23

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

Как использовать мьютексы?

Я нашел как минимум 2 способа как их можно использовать. Через Thandle и через TMutex из syncobjs. Причем, при использовании TMutex также есть варианты – один вариант захватывать участок кода через Acquire, а другой вариант – захватывать участок кода через WaitFor(). Все это мы посмотрим ниже на примерах, а пока рассмотрим общие схемы…

1 вариант через THandle

Пример использования Мьютекса через THandle

Напишем простую программу, которая будет записывать в текстовый файл слово ‘sometext ‘ в количестве 50000 раз. Сама запись будет происходить по нажатию кнопки Start_Writing_To_FileClick. Прогресс записи будет отражаться в файле ProgressBar.

Смысл программы в том, чтобы запустить 2 экземпляра этой программы и нажать на Start_Writing_To_FileClick в обоих экземплярах. В результате, если не отрегулировать потоки – запись будет вестись одновременно из 2 потоков, что приведет к непредсказуемым результатам.

24

В uses добавим

В секции type объявляем свой класс потока

В глобальных переменных пишем

В FormCreate пишем…

В FormDestroy

Далее создадим специальную процедуру, которую будем вызывать в теле потока…

Далее, обработаем метод execute потока

Далее, непосредственно запускаем поток

Тестируем пример

Запускаем 2 копии программы, на одной из них жмем на кнопку – поток запускается. В это же время пытаемся запустить тот же поток из второго экземпляра программы – не запускается.

25

 

Когда поток первой копии программы отработал до конца – запускается второй. Это видно на рисунке ниже.

26

 

Вот так отработали потоки в моем самом простом эксперименте. Мы использовали мьютексы при помощи THandle. Это, как я понимаю более старый вариант по сравнению с TMutex, поэтому я и использовал имя для переменной мьютекса oldMutex.

Пример использования Мьютекса через TMutex

Теперь сделаем почти тоже самое, только будем использовать TMutex

В uses добавим

В type

В глобальные переменные

В FormCreate

В FormDestroy

Далее подкорректируем немного процедуру вывода результата

Далее обработаем execute

Далее вызовем через кнопку этот поток

Тестирование покажет те же результаты. Пробуем нажать на кнопку во втором экземпляре программы и ничего.

25

 

Когда заканчивает работать первый поток, автоматически запускается второй

26

 

Если использовать функцию WaitFor() вместо Acuire, то мы сможем регулировать время ожидания вторым потоком, например WaitFor(2000) – тогда поток из второго экземпляра будет ждать не до конца выполнения, а только 2 секунды.

У меня при тестировании происходила такая картина, если у второй программы нажать на кнопку несколько раз, то поток выполнялся несколько раз во 2-м экземпляре программы. Так вот, чтобы это предотвратить – можно использовать WaitFor() с каким либо числовым параметром, не INFINITE. Скажем WaitFor(2000).

В принципе мы рассмотрели основные методы применения Мьютекса. Узнали что это – написали простейшие программы и протестировали их.

Самое главное запомнить про мютексы, что они нужны для управления потоками из разных процессов, например из 2 копий программ.

А вот что записалось у нас в файл

27

This entry was posted in Delphi, Потоки(Threads). Bookmark the permalink.