Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.116.90.141] |
|
Страницы: (4) [1] 2 3 ... Последняя » все ( Перейти к последнему сообщению ) |
Сообщ.
#1
,
|
|
|
Всем привет!
Не могу понять – где косяк в этом коде? Запускаю, выводится только: THREADS ARE CREATED AND RUNNING !!! PRESS A KEY TO PAUSE 'READ' THREAD... Write function thread... Прикол в том, что если закомментить EnterCriticalSection и LeaveCriticalSection, всё работает чётко... #include <stdio.h> #include <windows.h> #include <conio.h> #define CRITICAL_SECTIONS // нужны ли критические секции (закомментируйте эту строку, если секции не нужны) #define THREAD_COUNT 2 // кол-во потоков #define FINISH_THREADS_WAIT_TIME 5000 // время ожидания завершения потоков по умолчанию (миллисекунд) enum ThreadType { READ_THREAD, WRITE_THREAD }; // тип потока (читающий, записывающий), должно соответствовать кол-ву THREAD_COUNT enum ThreadStatus { THREAD_STOPPED, THREAD_RUNNING, THREAD_PAUSED }; // типы статусов работы потоков (остановлен, работает, на паузе) // Структура с информацией о потоках struct ThreadInfo { ThreadStatus Status[THREAD_COUNT]; // статусы работы потоков HANDLE hThread[THREAD_COUNT]; // хендлы потоков HANDLE hEvent[THREAD_COUNT]; // хендлы событий о паузах #ifdef CRITICAL_SECTIONS CRITICAL_SECTION CriticalSection; // объект критической секции #endif }; ThreadInfo ioThreads; // информация о потоках void ReadProc(); void WriteProc(); void (*ioThreadProc[THREAD_COUNT])() = {ReadProc, WriteProc}; // Указатели на функции, выполняемые в потоке (вызываются из главной функции потока) //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Главная функция потока // lpParam - номер потока (от 0 до THREAD_COUNT-1) DWORD WINAPI MainThreadProc(LPVOID lpParam) { do { // Обработка паузы while (ioThreads.Status[(int)lpParam] == THREAD_PAUSED) { if (ioThreads.hEvent[(int)lpParam]) { WaitForSingleObject(ioThreads.hEvent[(int)lpParam], INFINITE); } else { Sleep(1); } }; // Выполнение основной процедуры цикла ioThreadProc[(int)lpParam](); } while (ioThreads.Status[(int)lpParam] != THREAD_STOPPED); ExitThread(0); } // Атомарное (безопасное в плане межпотокового взаимодействия) сравнение var с expected и запись в var значения desired в случае совпадения // Возвращает TRUE, если запись произошла (т.е. var был равен expected); FALSE, если var не равен expected bool AtomicCompareAndExchange(int* var, int expected, int desired) { return __atomic_compare_exchange_n(var, &expected, desired, FALSE, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); } // Создание потоков и событий // Возвращает кол-во успешно созданных потоков (должно быть THREAD_COUNT) int CreateThreads() { ZeroMemory(&ioThreads, sizeof(ioThreads)); int total = 0; for (int i=0; i<THREAD_COUNT; ++i) { ioThreads.hThread[i] = CreateThread(NULL, 0, &MainThreadProc, LPVOID(i), 0, NULL); if (ioThreads.hThread[i]) { ioThreads.hEvent[i] = CreateEvent(NULL, FALSE, TRUE, NULL); ioThreads.Status[i] = THREAD_RUNNING; total++; } } #ifdef CRITICAL_SECTIONS InitializeCriticalSection(&ioThreads.CriticalSection); #endif return total; } // Поставить поток tt на паузу void PauseThread(ThreadType tt) { if (AtomicCompareAndExchange((int*)&ioThreads.Status[tt], THREAD_RUNNING, THREAD_PAUSED) and ioThreads.hEvent[tt]) { ResetEvent(ioThreads.hEvent[tt]); } } // Продолжить работу потока tt void ContinueThread(ThreadType tt) { if (AtomicCompareAndExchange((int*)&ioThreads.Status[tt], THREAD_PAUSED, THREAD_RUNNING) and ioThreads.hEvent[tt]) { SetEvent(ioThreads.hEvent[tt]); } } // Завершить работу потока tt (вернее, просигнализировать фукнции MainThreadProc, что нужно завершать работу) void StopThread(ThreadType tt) { ioThreads.Status[tt] = THREAD_STOPPED; } // Завершить потоки (сняв их с паузы, если они на паузе) // Максимальное время ожидания задаётся в Wait (по умолчанию FINISH_THREADS_WAIT_TIME секунд) // Force - нужно ли уничтожать потоки, если время ожидания истекло (!!!опасно!!!) // Возвращает TRUE, если дождался; FALSE, если нет bool FinishThreads(int Wait = FINISH_THREADS_WAIT_TIME, bool Force = FALSE) { for (int i=0; i<THREAD_COUNT; ++i) { StopThread(ThreadType(i)); } int result = WaitForMultipleObjects(THREAD_COUNT, ioThreads.hThread, TRUE, Wait); // ждём завершения потоков for (int i=0; i<THREAD_COUNT; ++i) { if (ioThreads.hThread[i]) { if (Force && (result != WAIT_OBJECT_0)) TerminateThread(ioThreads.hThread[i], 0); CloseHandle(ioThreads.hThread[i]); } if (ioThreads.hEvent[i]) CloseHandle(ioThreads.hEvent[i]); } #ifdef CRITICAL_SECTIONS DeleteCriticalSection(&ioThreads.CriticalSection); #endif ZeroMemory(&ioThreads, sizeof(ioThreads)); return (result == WAIT_OBJECT_0); } // Завершить потоки (предварительно сняв с паузы), уничтожив их, если время ожидания Wait истекло // !!! Используйте эту функцию с осторожностью, только в том случае, если иного выхода нет, поскольку если // какой-либо из потоков не завершился в течение таймаута Wait, скорее всего связано с ошибкой в работе потоков !!! bool ForceFinishThreads(int Wait = FINISH_THREADS_WAIT_TIME) { FinishThreads(Wait, TRUE); } #ifdef CRITICAL_SECTIONS // Начало критического кода потока void StartCriticalCode() { EnterCriticalSection(&ioThreads.CriticalSection); } // Окончание критического кода потока void EndCriticalCode() { LeaveCriticalSection(&ioThreads.CriticalSection); } #endif //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Функция чтения void ReadProc() { StartCriticalCode(); printf("Read function thread...\n"); EndCriticalCode(); Sleep(300); } // Функция чтения void WriteProc() { StartCriticalCode(); printf("Write function thread...\n"); EndCriticalCode(); Sleep(700); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void pause() { int c = getch(); if (!c || c == 224) getch(); } int main() { // Создание и запуск потоков if (CreateThreads() != THREAD_COUNT) { printf("ERROR CREATING THREADS :(\n"); ForceFinishThreads(); // на всякий случай, вдруг только один создался pause(); return 1; }; printf("THREADS ARE CREATED AND RUNNING !!!\n"); printf("PRESS A KEY TO PAUSE 'READ' THREAD...\n"); pause(); PauseThread(READ_THREAD); printf("PRESS A KEY TO PAUSE 'WRITE' THREAD...\n"); pause(); PauseThread(WRITE_THREAD); printf("PRESS A KEY TO CONTINUE 'READ' THREAD...\n"); pause(); ContinueThread(READ_THREAD); printf("PRESS A KEY TO CONTINUE 'WRITE' THREAD...\n"); pause(); ContinueThread(WRITE_THREAD); printf("PRESS A KEY TO FINISH...\n"); pause(); if (ForceFinishThreads()) { printf("THREADS ARE FINISHED SUCCESSFULLY! :)\n"); } else { printf("ERROR STOPPING THREADS :(\n"); } printf("PRESS A KEY TO QUIT...\n"); pause(); return 0; } |
Сообщ.
#2
,
|
|
|
Ну, как минимум не следует делать им инициализацию после запуска рабочих ниток.
|
Сообщ.
#3
,
|
|
|
Цитата Qraizer @ Ну, как минимум не следует делать им инициализацию после запуска рабочих ниток. Там и события тоже после запуска создаются. Цитата Jin X @ Не могу понять – где косяк в этом коде? Как только ты вызываешь CreateThread, у тебя запускается новый поток. Его код будет исполнен раньше, чем вызовется следующая строчка в программе. Соответственно все объекты, которые используются в функции MainThreadProc, должны быть созданы и проинициализированы до этого вызова. |
Сообщ.
#4
,
|
|
|
Цитата Qraizer @ Блин, точно ж! Прогон Ну, как минимум не следует делать им инициализацию после запуска рабочих ниток. Цитата Олег М @ А события там и не используются раньше, чем будет вызвано PauseThread. Там и события тоже после запуска создаются. Добавлено Теперь всё ок, работает |
Сообщ.
#5
,
|
|
|
Цитата Jin X @ Цитата Qraizer @ Блин, точно ж! Ну, как минимум не следует делать им инициализацию после запуска рабочих ниток. Но если очень хочется сначала создать поток, а потом всё остальное, создай его с параметром CREATE_SUSPENDED (5-й параметр). Когда всё будет готово, запустишь поток посредством "ResumeThread". |
Сообщ.
#6
,
|
|
|
Цитата Jin X @ Хотя во избежание косяков в будущем, лучше сделать заранее (вернее, даже CREATE_SUSPENDED, а потом ResumeThread) А события там и не используются раньше, чем будет вызвано PauseThread. Добавлено ЫукпШ, |
Сообщ.
#7
,
|
|
|
Цитата Jin X @ Хотя во избежание косяков в будущем, лучше сделать заранее (вернее, даже CREATE_SUSPENDED, а потом ResumeThread) Я лично плохо представляю для чего может понадобиться создавать поток с CREATE_SUSPENDED. Кстати в твоём случае эти статусы вообще не нужны. Достаточно только события и булевской переменной volatile bool Stop, даже не atomic. Хотя не очень понятно, что ты пытаешься сделать. |
Сообщ.
#8
,
|
|
|
Можно без статусов, но решил сделать так.
Чуть меньше накладных расходов (на вызов WaitForSingleObject) + лишняя подстраховка на случай ошибки создания ивента (хотя я понимаю, что это кране маловероятная ситуация). Код получился больше и сложнее, но мне так нравится. А CREATE_SUSPENDED – чтобы не создавать ивенты лишний раз, если вдруг возникла ошибка создания потока (тоже маловероятно, но всё же предусмотреть это стоит). |
Сообщ.
#9
,
|
|
|
Цитата Олег М @ Цитата Jin X @ Хотя во избежание косяков в будущем, лучше сделать заранее (вернее, даже CREATE_SUSPENDED, а потом ResumeThread) Я лично плохо представляю для чего может понадобиться создавать поток с CREATE_SUSPENDED. Поскольку такая возможность существует, значит у разработчиков были основания. ----- Пожалуйста, пример: 1. Делаем много-процессное приложение. 2. Главный процесс может запустить несколько дочерних процессов. Он делает какую-то предварительную работу - например готовит файл. (Снимок экрана). Далее взводит событие, и запущенный дочерний процесс просыпается своим потоком и отправляет на сервер приготовленный файл. 3. Хотелось бы все эти мероприятия решить в общем виде, оформить классом и сложить в библиотеку. 4. Вопрос - какое имя будет у объекта синхронизации ? (Например, эвента). --- Делаем так - придумаем любое имя. Сообщим его дочернему процессу через командную строку. Дочерний процесс добавит к имени свой идентификатор в текстовом виде. Чтобы для разных дочерних процессов имена не совпадали. Но как его узнать родителю, чтобы создать объект синхронизации ? Используем CREATE_SUSPENDED. Процесс создан, (но его поток ещё не запущен). После этого ID дочернего у родителя есть. Теперь родитель делает эвент и запускает поток дочернего процесса. Ребёнок получает доступ к эвенту, засыпает на нём и ждёт команды... ----- Ещё пример: 1. Пишу некую программу-лоадер, которая запустит другую программу. 2. С флагом CREATE_SUSPENDED. 3. Процесс в памяти, не запущен, дочерний. 4. Это значит, что я могу корректировать коды дочернего процесса как захочу. 5. Программа-лоадер производит коррекции кодов дочернего процесса и запускает его. 6. После коррекции дочерний процесс ведёт себя иначе, чем планировал разработчик.. |
Сообщ.
#10
,
|
|
|
Как-то неубедительно. Всё это скорее "о! я придумал, где это можно применить", чем "о да, это клёвый сценарий". ID к примеру, легко заменяются инкрементируемыми для каждого потомка int-ами. Патчить код проще, прикинувшись отладчиком, всё равно и для того, и для другого нужны высокие привилегии.
Я вот тоже чуть не написал, что если нужно создать мульён потоков, но застартить их единомоментно, то CREATE_SUSPENDED подойдёт. Нет, не подойдёт, будить придётся всё равно в цикле. А правильно – сделать эвент "Go!Go!Go!", нехай после старта ждут, а опосля взвести его одной функцией. Добавлено И выходом командовать глобальным булевым признаком как-то некрасиво. Делаем опять же эвент, нехай его ждут WaitForMultiple..., всё равно нитки ждут операционных событий, от ещё одного эвента не убудет, и в нужный момент Set ему. |
Сообщ.
#11
,
|
|
|
Цитата Qraizer @ Ну а что плохого? То, что глобальная? А если в класс завернуть? И выходом командовать глобальным булевым признаком как-то некрасиво. Но в целом решение с Multiple'ом нормальное |
Сообщ.
#12
,
|
|
|
Цитата Jin X @ Можно без статусов, но решил сделать так. Чуть меньше накладных расходов (на вызов WaitForSingleObject) + лишняя подстраховка на случай ошибки создания ивента (хотя я понимаю, что это кране маловероятная ситуация). Код получился больше и сложнее, но мне так нравится. Что-то не вижу у тебя этой подстраховки. Да и накладных расходов у тебя там получается на порядок больше. Думаешь atomic-операции они бесплатные? Добавлено Цитата ЫукпШ @ Поскольку такая возможность существует, значит у разработчиков были основания. Думаю, основание там было одно - раз это сделать легко, значит надо сделать, авось потом кто-нибудь придумает зачем. Цитата ЫукпШ @ Теперь родитель делает эвент и запускает поток дочернего процесса. А как запустить поток в другом процессе? |
Сообщ.
#13
,
|
|
|
Цитата Олег М @ А как запустить поток в другом процессе? Всё так же. Если я создаю дочерний процесс (с флагом CREATE_SUSPENDED), то я могу запустить главный поток посредством "ResumeThread". Поскольку хэндл потока у меня будет после создания. --- Останавливать/запускать можно любой поток, надо иметь хэндл ("рукоятка, чтобы порулить") и права доступа. По отношению к дочернему процессу имеются достаточные права доступа, с ним можно сделать практически всё-что-угодно. --- Если ты хочешь "RemoteThread", то это сложнее. В Сети есть хорошие статьи на эту тему. |
Сообщ.
#14
,
|
|
|
Цитата Олег М @ Например:Что-то не вижу у тебя этой подстраховки. if (ioThreads.hEvent[(int)lpParam]) { WaitForSingleObject(ioThreads.hEvent[(int)lpParam], INFINITE); } else { Sleep(1); } Цитата Олег М @ А где ты видишь в цикле atomic-операции? Да и накладных расходов у тебя там получается на порядок больше. Думаешь atomic-операции они бесплатные? Добавлено А избавиться от atomic, если уж на то пошло, можно разделив стоп-флаг и флаг паузы на 2 части. Но тут особого смысла нет, т.к. пауза используется редко. Хотя было бы чуть проще в реализации, конечно. |
Сообщ.
#15
,
|
|
|
Цитата Jin X @ Например: if (ioThreads.hEvent[(int)lpParam]) { WaitForSingleObject(ioThreads.hEvent[(int)lpParam], INFINITE); } else { Здесь лучше проверять, что возвращает WaitForSingleObject. А ещё лучше, если CreateEvent вернул ошибку, вообще не запускать поток. |