Потоки... Потоки? Потоки!, FPC |
Потоки... Потоки? Потоки!, FPC |
Archon |
27.06.2009 21:13
Сообщение
#1
|
Профи Группа: Пользователи Сообщений: 618 Пол: Мужской Репутация: 24 |
Сел переписывать движок Doomed Game под потоки и... понял, что мои знания о потокобезопасности оставляют желать лучшего. Может быть кто-нибудь (volvo?) проведет несколько лекций на эту тему? Думаю, это не только меня может заинтересовать. Или может я просто задам свои вопросы?
-------------------- Close the World...txeN eht nepO
|
volvo |
27.06.2009 21:20
Сообщение
#2
|
Гость |
Название темы -
Нет, пока лекций на тему потокобезопасности я читать не буду, лучше задавай вопросы, посмотрим, во что это выльется. Может вместе и смастерим какой-нибудь полезный FAQ, тем более на реальном примере... |
Archon |
27.06.2009 21:49
Сообщение
#3
|
Профи Группа: Пользователи Сообщений: 618 Пол: Мужской Репутация: 24 |
Окей. Тогда рассмотрим пример. Мое приложение состоит из нескольких глобальных объектов. Не знаю, насколько это оправдано, но в свое время мне это показалось неплохим решением. Объекты примерно такие:
unit u_test;Так как с объектами могут взаимодействовать (вызывать их методы) сразу несколько потоков, их следует сделать потокобезопасными. Предлагаю это сделать . Если такая схема приложения кажется тебе неподходящей, можно ее пересмотреть. -------------------- Close the World...txeN eht nepO
|
volvo |
27.06.2009 22:16
Сообщение
#4
|
Гость |
Какие методы планируется вызывать из разных потоков? Все? Как пытался сделать потокобезопасность? (ну, и более глобальный вопрос, как вообще обеспечивается межпоточное взаимодействие, знаешь?) Что именно (по-твоему) может быть опасного в тех методах, которые ты привел, почему нельзя прямо так взять и работать с объектом из разных потоков?
|
Archon |
27.06.2009 22:29
Сообщение
#5
|
Профи Группа: Пользователи Сообщений: 618 Пол: Мужской Репутация: 24 |
Цитата Какие методы планируется вызывать из разных потоков? Все? В общем случае не все. Но кто его знает, как потом понадобится. Поэтому давай попробуем сделать все.Цитата Как пытался сделать потокобезопасность? Пока никак не пытался, только доки читал.Цитата (ну, и более глобальный вопрос, как вообще обеспечивается межпоточное взаимодействие, знаешь?) Знаю на теоретическом уровне критические секции, события, мутексы, семафоры. Использовать не пробовал.Цитата Что именно (по-твоему) может быть опасного в тех методах, которые ты привел, почему нельзя прямо так взять и работать с объектом из разных потоков? Так... Думаю, проблемы могут возникнуть при обращении к данным (FData). Сам по себе вызов методов, кажется, безопасен (у потоков отдельные стеки). Причем если пересекутся две попытки записи, проблемы очевидны, но я слышал, что и чтение/запись тоже вызывает проблемы (рассинхронизация кэшей процессоров, например). Еще есть подозрение, что при наложении чтение/запись можно прочитать испорченное значение (вряд ли запись переменной происходит в 1 этап).PS Вообще, выкладываю и сам проект. Исходники надергал из Doomed Game, получилось подобие спрайтового движка. Он работает, но не потокобезопасен. Потому возможны ошибки. Несколько файлов с документацией удалять не стал, но они уже устарели. Прикрепленные файлы project_t_snapshot.zip ( 855.02 килобайт ) Кол-во скачиваний: 524 -------------------- Close the World...txeN eht nepO
|
volvo |
27.06.2009 22:37
Сообщение
#6
|
Гость |
Цитата Еще есть подозрение, что при наложении чтение/запись можно прочитать испорченное значение (вряд ли запись переменной происходит в 1 этап). Вот !!! Все, что нужно, ты уже сказал... Тебе надо просто гарантировать атомарность (неделимость) операции изменения значения переменной. Для этого вместо Inc() используй InterlockedIncrement() или InterlockedExchangeAdd(), обе функции описаны в System... Все Interlocked... функции гарантируют монопольное изменение значения переменной (к тому же, они и выполняются быстрее, Рихтер говорит о примерно 50 тактах против 1000, которые требуются для перехода в Kernel-mode из User-mode) |
Archon |
27.06.2009 22:45
Сообщение
#7
|
Профи Группа: Пользователи Сообщений: 618 Пол: Мужской Репутация: 24 |
Цитата Вот !!! Все, что нужно, ты уже сказал... Тебе надо просто гарантировать атомарность (неделимость) операции изменения значения переменной. Для этого вместо Inc() используй InterlockedIncrement() или InterlockedExchangeAdd(), обе функции описаны в System... Все Interlocked... функции гарантируют монопольное изменение значения переменной (к тому же, они и выполняются быстрее, Рихтер говорит о примерно 50 тактах против 1000, которые требуются для перехода в Kernel-mode из User-mode) А неделимость чтения кто гарантировать будет? Вдруг запись произойдет в середине процесса чтения? К тому же, Inc - это только пример. Да и на месте Integer может стоять какой-нибудь TList.Я набросал примерно вот что в псевдокоде: unit u_test; Добавлено через 1 мин. Кстати, еще ведь есть и прямой доступ к полю через property , так что там не только Inc. Исправил: добавил else в условиях. Сообщение отредактировано: Archon - 27.06.2009 23:05 -------------------- Close the World...txeN eht nepO
|
volvo |
27.06.2009 23:25
Сообщение
#8
|
Гость |
Цитата Да и на месте Integer может стоять какой-нибудь TList. Тогда, естественно, придется пользоваться другими средствами синхронизации...Цитата Я набросал примерно вот что в псевдокоде: А чего в "псевдо"? Делай уже Паскалевский код, что там осталось... Только немного не так:Цитата try , если мьютекс уже установлен из другого потока, то будет облом, второй раз тебе установить его не дадут, и операция не произойдет Ты этого добивался, или тебе надо ждать, пока один закончит, и другой проделает эту операцию? Тогда CriticalSections в помощь... |
Archon |
27.06.2009 23:39
Сообщение
#9
|
Профи Группа: Пользователи Сообщений: 618 Пол: Мужской Репутация: 24 |
Цитата А чего в "псевдо"? Делай уже Паскалевский код, что там осталось... Я не знаю процедур . Можно, конечно, WinApi, но наверно и в rtl должны быть кроссплатформенные аналоги. Попробую поискать их завтра, сейчас уже спать хочу.Цитата Здесь ты в любом случае освобождаешь Mutex, а этого делать нельзя. Ибо освобождает его только тот, кто установил... Точно, спасибо.Цитата , если мьютекс уже установлен из другого потока, то будет облом, второй раз тебе установить его не дадут, и операция не произойдет Ты этого добивался, или тебе надо ждать, пока один закончит, и другой проделает эту операцию? Тогда CriticalSections в помощь... Облом будет, если мьютекс не освободится в течение N секунд, а в WinApi, например, есть для этой функции константа INFINITE.-------------------- Close the World...txeN eht nepO
|
Archon |
28.06.2009 6:20
Сообщение
#10
|
Профи Группа: Пользователи Сообщений: 618 Пол: Мужской Репутация: 24 |
Цитата , если мьютекс уже установлен из другого потока, то будет облом, второй раз тебе установить его не дадут, и операция не произойдет Ты этого добивался, или тебе надо ждать, пока один закончит, и другой проделает эту операцию? Тогда CriticalSections в помощь... Кстати да, CriticalSection тут будет проще .
unit u_test;Проверяй . Добавлено через 6 мин. Почитал про Interlocked-функции. Я так понимаю, что чтение 32-битной переменной в любом случае атомарно? PS Пока читал, узнал, что Int64 и QWord - оказывается не ordinal . -------------------- Close the World...txeN eht nepO
|
Archon |
28.06.2009 8:36
Сообщение
#11
|
Профи Группа: Пользователи Сообщений: 618 Пол: Мужской Репутация: 24 |
Обезопасил модуль u_log.pas. Теперь думаю над u_window.pas. Предпологается использовать этот объект только в одном потоке (в модуле u_graphics.pas), но WinProc - это же отдельный поток, верно? Значит вот так делать нельзя:
function TD3DWindow.MessageProc(Msg: UINT; WParam: WPARAM; LParam: LPARAM): LResult;Думаю, от этого вобще лучше избавиться и ловить клавиши исключительно в модуле u_controls.pas (его пока нет, но будет ) Сообщение отредактировано: Archon - 28.06.2009 8:37 Прикрепленные файлы u_log.pas ( 2.73 килобайт ) Кол-во скачиваний: 275 u_window.pas ( 11.97 килобайт ) Кол-во скачиваний: 447 -------------------- Close the World...txeN eht nepO
|
volvo |
28.06.2009 8:57
Сообщение
#12
|
Гость |
Цитата Проверяй А чего его проверять, вроде выглядит нормально, надо написать тестирующую программу, и запустить. Тогда увидим, правильно ли оно работает.Цитата Я так понимаю, что чтение 32-битной переменной в любом случае атомарно? И чтение и запись 32-битной переменной само по себе - атомарно. Но Interlocked-функции предназначены для того, чтобы сделать атомарными изменения этих переменных:Цитата(MSDN) This feature is useful in a multitasking operating system, in which the system can interrupt one thread's execution to grant a slice of processor time to another thread. Without such synchronization, two threads could read the same value, increment it by 1, and store the new value for a total increase of 1 instead of 2. The interlocked variable-access functions protect against this kind of error. Цитата Пока читал, узнал, что Int64 и QWord - оказывается не ordinal С этими типами вообще много неясного... Вот, к примеру:Цитата(ref.pdf 3.1.1) Ordinal types are countable and ordered, i.e. it is, in principle, possible to start counting them , однако и Int64 и QWord прекрасно Inc-рементируются Да и Pred/Succ, которые тоже работают только с перечислимыми типами, с ними работают. А вот цикл for с управляющей переменной Int64/QWord невозможен. Как это понимать?one bye one, in a specified order. This property allows the operation of functions as Inc, Ord, Dec on ordinal types to be defined. |
Archon |
28.06.2009 9:03
Сообщение
#13
|
Профи Группа: Пользователи Сообщений: 618 Пол: Мужской Репутация: 24 |
Цитата А чего его проверять, вроде выглядит нормально, надо написать тестирующую программу, и запустить. Тогда увидим, правильно ли оно работает. А какие существуют методы тестирования на потокобезопасность?-------------------- Close the World...txeN eht nepO
|
Archon |
28.06.2009 9:43
Сообщение
#14
|
Профи Группа: Пользователи Сообщений: 618 Пол: Мужской Репутация: 24 |
Продолжаю исправлять классы. Работу с текстурами я просто сокрыл внутри спрайтов. Так что теперь главное - сделать безопасную работу со спрайтами.
TSprite = class(TGraphicObject)Предпологается, что один поток будет делать SetMode и SetParams, а другой Update и Draw. Критические секции сделать не проблема, но не будет ли это слишком медленным? Все таки каждый кадр предпологается рисовать множество этих спрайтов. -------------------- Close the World...txeN eht nepO
|
volvo |
28.06.2009 10:06
Сообщение
#15
|
Гость |
Что-то не так... Смотри:
uses sysutils, classes,- простейший тест, правда? Примерно одинаковое количество раз в файле должно присутствовать каждое сообщение. Теперь смотри на вывод: threads.txt ( 91.51 килобайт ) Кол-во скачиваний: 496 Самое интересное начинается с 89 строки... |
Archon |
28.06.2009 10:54
Сообщение
#16
|
Профи Группа: Пользователи Сообщений: 618 Пол: Мужской Репутация: 24 |
Странно, я у себя в том же тесте таких ужасов не наблюдаю. Только в конце 1 повторяется, но там их всего ~2-3 потоков осталось. Может менеджер процессов Windows шалит?
Прикрепленные файлы threads.txt ( 100.26 килобайт ) Кол-во скачиваний: 279 -------------------- Close the World...txeN eht nepO
|
volvo |
28.06.2009 11:29
Сообщение
#17
|
Гость |
Цитата Может менеджер процессов Windows шалит? Угу, тут прямо шалит, а если сделать:unit u_test;(код тестирующего приложения не меняется), то шалить моментально перестает? |
Archon |
28.06.2009 11:44
Сообщение
#18
|
Профи Группа: Пользователи Сообщений: 618 Пол: Мужской Репутация: 24 |
А в чем разница? Разве что в TMultiReadExclusiveWriteSynchronizer используется Mutex, а не CriticalSection.
Цитата о шалить моментально перестает? Неа, у меня на старой версии не шалило, а вот этот лог я получил при первой же проверке новой версии. Интересны строчкиЦитата 2009-06-28 14:35:14.692 Note: "13" - message #13 в конце лога. Сообщение отредактировано: Archon - 28.06.2009 11:45 Прикрепленные файлы threads.txt ( 261.63 килобайт ) Кол-во скачиваний: 282 -------------------- Close the World...txeN eht nepO
|
volvo |
28.06.2009 11:51
Сообщение
#19
|
Гость |
Цитата Разве что в TMultiReadExclusiveWriteSynchronizer используется Mutex, а не CriticalSection. Кто сказал?Цитата(rtl.pdf 37.53.3) Description: Create creates a new instance of TMultiReadExclusiveWriteSynchronizer. It initializes Насчет строчек "потока №13" - хм... Из 5409 строк лога этот поток завершает запись в 5289 строке. Это не конец лога совсем... Ты файлы не перепутал?a TRTLCriticalSection. Добавлено через 5 мин. Или ты о том, что время НЕпоследовательное, а вперемешку? Так это подразумевается вообще-то при использовании критических секций, порядком входа в секцию управлять нельзя. Вот что Рихтер говорит в частности про LeaveCriticalSection: Цитата Эта функция просматривает элементы структуры CRITICAL_SECTION и уменьшает счетчик числа захватов ресурса вызывающим потоком на 1. Если его значение больше 0, LeaveCriticalSection ничего не делает и просто возвращает управление. Если значение счетчика достигло 0, LeaveCriticalSection сначала выясняет, есть ли в системе другие потоки, ждущие данный ресурс в вызове EnterCriticalSection. Если есть хотя бы один такой поток, функция настраивает значения элементов структуры, что бы они сигнализировали о занятости ресурса, и отдает его одному из ждущих потоков (поток выбирается "по справедливости"). Если же ресурс никому не нужен, LeaveCriticalSection соответственно сбрасывает элементы структуры. |
Archon |
28.06.2009 12:47
Сообщение
#20
|
Профи Группа: Пользователи Сообщений: 618 Пол: Мужской Репутация: 24 |
Цитата TMultiReadExclusiveWriteSynchronizer is a default implementation of the IReadWriteSync Цитата из того же файла версии 2.1 за август 2006. У меня компилятор 2.0.4 (оказывается ).(1465) interface. It uses a single mutex to protect access to the read/write resource, resulting in a single thread having access to the resource. Цитата Насчет строчек "потока №13" - хм... Из 5409 строк лога этот поток завершает запись в 5289 строке. Это не конец лога совсем... Ты файлы не перепутал? Конец - это относительно. Глянь чуть выше на строчки 4642-4902. 4903-4926 тоже интересно. Такие последовательности появляются регулярно, могу еще логов предоставить .-------------------- Close the World...txeN eht nepO
|
Текстовая версия | 20.09.2024 18:13 |