Пётр
Есть статейка или видеурок по потокам? Не въезжаю, что такое мьютексы и семафоры, для чего нужны и как работают. Примеры из справки непонимаю
Параллельные потоки, мьютексы и семафоры
Сообщений 1 страница 21 из 21
Поделиться127.01.2011 14:06:43
Поделиться227.01.2011 14:51:17
Что не понятно: принцип построения программы или само понятие - потоки?
Вот одна и та же программа в потоке и без, попробуй перемещать по экрану...
Enumeration #Window_0 #Start #Text EndEnumeration Procedure Progr() For a=0 To 100000 SetGadgetText(#Text,Str(a)) Delay(100) Next EndProcedure OpenWindow(#Window_0, 100, 150, 400, 200, "Работа без потока",#PB_Window_SystemMenu|#PB_Window_WindowCentered) ButtonGadget(#Start,20,50,70,20,"Старт") TextGadget(#Text, 150,50, 30,20,"") Repeat Event = WaitWindowEvent() If Event = #PB_Event_Gadget Select EventGadget() Case #Start Progr() EndSelect EndIf Until Event = #PB_Event_CloseWindo
Enumeration #Window_0 #Start #Text EndEnumeration Procedure Progr(*x) For a=0 To 100000 SetGadgetText(#Text,Str(a)) Delay(100) Next EndProcedure OpenWindow(#Window_0, 100, 150, 400, 200, "Работа в потоке",#PB_Window_SystemMenu|#PB_Window_WindowCentered) ButtonGadget(#Start,20,50,70,20,"Старт") TextGadget(#Text, 150,50, 30,20,"") Repeat Event = WaitWindowEvent() If Event = #PB_Event_Gadget Select EventGadget() Case #Start If IsThread(ThreadID)=0 ThreadID=CreateThread(@Progr(), x) Else MessageRequester("", "Программа занята!", #MB_OK|#MB_ICONWARNING) EndIf EndSelect EndIf Until Event = #PB_Event_CloseWindow
Отредактировано mirashic (27.01.2011 15:06:21)
Поделиться327.01.2011 15:36:00
mirashic
Как создать и запустить поток знаю. Но управлять им, т.е. допустим корректно завершить Я объявляю глобальную переменную(флаг) и выставляю его допустим в единицу, в потоке постоянно приходиться проверять этот флаг, и если он равен 1 то завершить все операции в потоке(чтоб не потерять данные) и выйти ProcedureReturn. Но зачем тогда нужны мьютексы и семафоры?
Поделиться427.01.2011 16:23:06
Мьютексы нужны для предотвращения конфликтов при коллективном (одновременном) доступе к ресурсу.
Вот пример из справки:
Procedure WithoutMutex(*Number) Shared Mutex For a = 1 To 5 ;LockMutex(Mutex) ; uncomment this to see the difference PrintN("Thread "+Str(*Number)+": Trying to print 5x in a row:") For b = 1 To 5 Delay(50) PrintN("Thread "+Str(*Number)+" Line "+Str(b)) Next b ;UnlockMutex(Mutex) ; uncomment this to see the difference Next a EndProcedure OpenConsole() Mutex = CreateMutex() thread1 = CreateThread(@WithoutMutex(), 1) Delay(25) thread2 = CreateThread(@WithoutMutex(), 2) Delay(25) thread3 = CreateThread(@WithoutMutex(), 3) WaitThread(thread1) WaitThread(thread2) WaitThread(thread3) Input()
Если функции LockMutex и UnlockMutex закомментированы, то в консоль выводится инфа в в беспорядочном виде, но стоит разкомментировать функции, то данные выводятся в строгом порядке.
В данном случае, работает только поток, захвативший Mutex, (LockMutex) а остальные ждут своей очереди пока не освободится Mutex (UnlockMutex).
Использование Mutex'ов желательно, скажем, при работе нескольких потоков с одним динамическим списком, чтобы не попортились данные в списке.
Поделиться527.01.2011 18:05:40
Примерно понял, но на ум, не приходит практического применения , и по мьютексу ещё один вопрос, LockMutex и UnlockMutex должны быть обязательно, внутри процедуры, или нет? Всё же хотелось бы увидеть реальный пример применения мьютекса и семафора. И про коректное завершение потока я правильно делаю, устанавливая флаг, или есть более изящный способ.
Поделиться627.01.2011 18:26:39
и по мьютексу ещё один вопрос, LockMutex и UnlockMutex должны быть обязательно, внутри процедуры, или нет
Они должны быть в процедуре, выполняемой в отдельном потоке.
И про коректное завершение потока я правильно делаю, устанавливая флаг, или есть более изящный способ.
Ну наверное правильно, но без примера кода сложно сказать...
Поток можно "убить" с помощью KillThread, но это не безопасно в том, плане, что поток "убивается" сразу же, т. е. если при завершении потока нужно освободить память или др. объекты, то убив поток, этого не произойдет, и из-за этого, могут появиться различные глюки!
Немедленно убивает заданный поток, ранее созданный с помощью функции CreateThread(). Эта функция очень опасна и должна использоваться как можно реже. Проблема в том, что поток немедленно убивается и не получает шанса выполнить какой-либо код завершения (например освободить память, удалить элементы, освободить собственный стек).
По возможности следует использовать флаг в виде глобальной переменной чтобы сказать потоку заканчивать выполнение (при этом производятся нужные завершающие действия), и только если это невозможно по какой-то причине следует использовать эту функцию.
Вообще, функцию KillThread нужно использовать только в крайнем случае, скажем, если поток повис и поэтому с помощью флага не завершить его работу.
Поделиться727.01.2011 18:46:42
Вот кстати пример с динамическим списком показывающий как решить вопрос коллизий при работе с ним.
Global Semaphore = CreateSemaphore() Global Mutex = CreateMutex() Global NewList Queue() Procedure Producer(Total) For i = 1 To Total Delay(Random(750) + 250) ; Блокируем остальные потоки (в т. ч. и основной) чтобы не произошло коллизий при работе с динамическим списком LockMutex(Mutex) LastElement(Queue()) AddElement(Queue()) Queue() = i UnlockMutex(Mutex) ; Разблокируем остальные потоки. ; Подаём сигнал что нужно обработать данные SignalSemaphore(Semaphore) Next i EndProcedure CreateThread(@Producer(), 30) For i = 1 To 30 ; Ждем сигнала от дополнительного потока WaitSemaphore(Semaphore) ; Блокируем остальные потоки, использующие данный Mutex, чтобы не произошло коллизий при работе с динамическим списком LockMutex(Mutex) Queue$ = "Queue:" ForEach Queue() Queue$ + " " + Str(Queue()) Next Queue() Debug Queue$ ; remove head element from the queue FirstElement(Queue()) DeleteElement(Queue()) UnlockMutex(Mutex) ; Разблокируем остальные потоки. Next i
Во время работы с динамическим списком, с помощью мьютексов, останавливаются все потоки, в которых используется данный мьютекс.
Далее, в основном потоке, происходит ожидание сигнала семафора, означающего что данные готовы к обработке. Этот сигнал устанавливается в дополнительном потоке.
Поделиться827.01.2011 18:51:33
Придумал применение LockMutex и UnlockMutex, завтра буду пробовать. Ещё про семафоры разжуйте, а то вообще тёмный лес.
Поделиться927.01.2011 19:00:06
Пока писал пример появился. В примере
Блокируем остальные потоки
вопрос какие остальные, если CreateThread, вызывается один раз?
Поделиться1027.01.2011 19:04:59
вопрос какие остальные, если CreateThread, вызывается один раз?
В том примере два потока, основной и дополнительный, созданный с помощью CreateThread.
Там, данные генерируются в дополнительном потоке, а основной (с помощью WaitSemaphore) ожидает их появления.
Как в основном, так и дополнительном потоках, осуществляется работа с динамическим списком, а мьютексы, блокируют один из потоков чтобы не произошла такая ситуация, что основной дополнительный поток создал элемент в списке, а основной его тут же удалил что приведет к ошибке в программе.
Поделиться1127.01.2011 19:10:52
Пётр
Не понял, ткни носом где начинается основной, где дополнительный?
Поделиться1227.01.2011 19:17:19
Дополнительный поток:
Procedure Producer(Total) For i = 1 To Total Delay(Random(750) + 250) ; Блокируем остальные потоки (в т. ч. и основной) чтобы не произошло коллизий при работе с динамическим списком LockMutex(Mutex) LastElement(Queue()) AddElement(Queue()) Queue() = i UnlockMutex(Mutex) ; Разблокируем остальные потоки. ; Подаём сигнал что нужно обработать данные SignalSemaphore(Semaphore) Next i EndProcedure
Основной поток:
CreateThread(@Producer(), 30) For i = 1 To 30 ; Ждем сигнала от дополнительного потока WaitSemaphore(Semaphore) ; Блокируем остальные потоки, использующие данный Mutex, чтобы не произошло коллизий при работе с динамическим списком LockMutex(Mutex) Queue$ = "Queue:" ForEach Queue() Queue$ + " " + Str(Queue()) Next Queue() Debug Queue$ ; remove head element from the queue FirstElement(Queue()) DeleteElement(Queue()) UnlockMutex(Mutex) ; Разблокируем остальные потоки. Next i
Поделиться1328.01.2011 12:22:43
Пётр
В упор не вижу второго потока, или подразумевается второй поток For i = 1 To 30 - Next i?
Поделиться1428.01.2011 12:36:48
lakomet
любой код, выполняемый в твоей программе, это и есть уже созданный первый (основной) поток.
Для примера, для чего нужны Мьютексы, например запущены два поток, кот. периодически будут записывать данные а один ЛистИконГаджет, если один поток обратится к гаджету именно в тот момент когда ID гаджета "открыто" вторым потоком (используется/записывается в гаджет), то произойдет вылет программы с ошибкой. Для это нужно использовать, например Мьютекс, чтобы Мьютекс "закрывался" когда один из потоков использует гаджет, а после окончания записи в гаджет "открывался", для того чтобы мог туда писать другой поток....
.... примерно так
Поделиться1528.01.2011 13:24:35
В упор не вижу второго потока, или подразумевается второй поток For i = 1 To 30 - Next i?
Сама программа это основной поток, а процедура Producer() - дополнительный поток, в итоге, 2 потока!
будут записывать данные а один ЛистИконГаджет, если один поток обратится к гаджету именно в тот момент когда ID гаджета "открыто" вторым потоком (используется/записывается в гаджет), то произойдет вылет программы с ошибкой
Не факт.
Дело в том, что происходит не прямая запись в гаджет, а посылка сообщения в обработчик событий гаджета, с помощью SendMessage или PostMessage (это в винде). Так что сообщения будут обработаны по очереди, а не одновременно.
Поделиться1628.01.2011 13:31:24
Ну допустим есть три кнопки a,b,c и три процедуры A(),B(),C(). Нажатием кнопки a запускаем поток A(), b запускаем поток B(), c запускаем поток C(), используя мьютекс в потоке B(), я могу запретить выполнение потоков A() и C()?
Поделиться1728.01.2011 13:39:25
Не факт.
почему привел именно этот пример: была именно такая ситуация, и мьютекс помог.
PS: сначала долго не мог понять почему прога вылетала, могла отработать без проблемм долго, а то вдруг дохла (очень редко могли они одновременно попасть), в итоге пришел к тому что именно в такой момент (одновременное обращение к гаджету), вылетала прога... сори привести именно тот пример не могу, было давно это... спорить не буду, но мне помог мьютекс, именно "закрывающийся" до команды "запись в гаджет данных" и тут же после команды 'мьютекс на открытие'. (до мьютекса безопасный поток был тоже включен).
Поделиться1809.09.2012 13:06:19
Внимание, грабли!
Не надейтесь на то, что при завершении потока, мьютекс разблокирует система (в теории так должно быть).
На практике этого не происходит.
Global Mutex = CreateMutex() Procedure Thread(*x) LockMutex(Mutex) EndProcedure CreateThread(@Thread(), 0) Delay(1000) LockMutex(Mutex) MessageRequester("","OK")
Поделиться1906.11.2023 01:29:13
Эх, спасибо за куриво
Поделиться2006.11.2023 01:30:13
мьютекс разблокирует система
В 5.4 и 6.0 исправили?
Поделиться2106.11.2023 14:33:25
В 5.4 и 6.0 исправили?
Это не ошибка PB.
Мьютекс нужно разблокировать.
Global Mutex = CreateMutex() Procedure Thread(*x) LockMutex(Mutex) ; код UnlockMutex(Mutex) EndProcedure CreateThread(@Thread(), 0) Delay(1000) LockMutex(Mutex) MessageRequester("","OK")