На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Обратите внимание:
1. Прежде чем начать новую тему или отправить сообщение, убедитесь, что вы не нарушаете правил форума!
2. Обязательно воспользуйтесь поиском. Возможно, Ваш вопрос уже обсуждали. Полезные ссылки приведены ниже.
3. Темы с просьбой выполнить какую-либо работу за автора в этом разделе не обсуждаются.
4. Используйте теги [ code=cpp ] ...текст программы... [ /code ] для выделения текста программы подсветкой.
5. Помните, здесь телепатов нет. Старайтесь формулировать свой вопрос максимально грамотно и чётко: Как правильно задавать вопросы
6. Запрещено отвечать в темы месячной и более давности без веских на то причин.

Полезные ссылки:
user posted image FAQ Сайта (C++) user posted image FAQ Форума user posted image Наши Исходники user posted image Поиск по Разделу user posted image MSDN Library Online (Windows Driver Kit) user posted image Google

Ваше мнение о модераторах: user posted image B.V.
Модераторы: B.V.
Страницы: (4) [1] 2 3 ... Последняя » все  ( Перейти к последнему сообщению )  
> Критические секции , крэш
    Всем привет!
    Не могу понять – где косяк в этом коде?
    Запускаю, выводится только:
    ExpandedWrap disabled
      THREADS ARE CREATED AND RUNNING !!!
      PRESS A KEY TO PAUSE 'READ' THREAD...
      Write function thread...
    И затем крэш.
    Прикол в том, что если закомментить EnterCriticalSection и LeaveCriticalSection, всё работает чётко...

    ExpandedWrap disabled
      #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;
      }
      Ну, как минимум не следует делать им инициализацию после запуска рабочих ниток.
        Цитата Qraizer @
        Ну, как минимум не следует делать им инициализацию после запуска рабочих ниток.

        Там и события тоже после запуска создаются.

        Цитата Jin X @
        Не могу понять – где косяк в этом коде?


        Как только ты вызываешь CreateThread, у тебя запускается новый поток. Его код будет исполнен раньше, чем вызовется следующая строчка в программе.
        Соответственно все объекты, которые используются в функции MainThreadProc, должны быть созданы и проинициализированы до этого вызова.
        Сообщение отредактировано: Олег М -
          Цитата Qraizer @
          Ну, как минимум не следует делать им инициализацию после запуска рабочих ниток.
          Блин, точно ж! Прогон :wall:

          Цитата Олег М @
          Там и события тоже после запуска создаются.
          А события там и не используются раньше, чем будет вызвано PauseThread.

          Добавлено
          Теперь всё ок, работает :)
            Цитата Jin X @
            Цитата Qraizer @
            Ну, как минимум не следует делать им инициализацию после запуска рабочих ниток.
            Блин, точно ж! :wall:

            Но если очень хочется сначала создать поток, а потом всё остальное,
            создай его с параметром CREATE_SUSPENDED (5-й параметр).
            Когда всё будет готово, запустишь поток посредством "ResumeThread".
              Цитата Jin X @
              А события там и не используются раньше, чем будет вызвано PauseThread.
              Хотя во избежание косяков в будущем, лучше сделать заранее (вернее, даже CREATE_SUSPENDED, а потом ResumeThread)

              Добавлено
              ЫукпШ, :D
                Цитата Jin X @
                Хотя во избежание косяков в будущем, лучше сделать заранее (вернее, даже CREATE_SUSPENDED, а потом ResumeThread)

                Я лично плохо представляю для чего может понадобиться создавать поток с CREATE_SUSPENDED.

                Кстати в твоём случае эти статусы вообще не нужны. Достаточно только события и булевской переменной volatile bool Stop, даже не atomic. Хотя не очень понятно, что ты пытаешься сделать.
                  Можно без статусов, но решил сделать так.
                  Чуть меньше накладных расходов (на вызов WaitForSingleObject) + лишняя подстраховка на случай ошибки создания ивента (хотя я понимаю, что это кране маловероятная ситуация).
                  Код получился больше и сложнее, но мне так нравится.

                  А CREATE_SUSPENDED – чтобы не создавать ивенты лишний раз, если вдруг возникла ошибка создания потока (тоже маловероятно, но всё же предусмотреть это стоит).
                    Цитата Олег М @
                    Цитата Jin X @
                    Хотя во избежание косяков в будущем, лучше сделать заранее (вернее, даже CREATE_SUSPENDED, а потом ResumeThread)

                    Я лично плохо представляю для чего может понадобиться создавать поток с CREATE_SUSPENDED.

                    Поскольку такая возможность существует, значит у разработчиков были основания.
                    -----
                    Пожалуйста, пример:
                    1. Делаем много-процессное приложение.
                    2. Главный процесс может запустить несколько дочерних процессов.
                    Он делает какую-то предварительную работу - например готовит файл.
                    (Снимок экрана). Далее взводит событие, и запущенный дочерний процесс
                    просыпается своим потоком и отправляет на сервер приготовленный файл.
                    3. Хотелось бы все эти мероприятия решить в общем виде, оформить классом
                    и сложить в библиотеку.
                    4. Вопрос - какое имя будет у объекта синхронизации ? (Например, эвента).
                    ---
                    Делаем так - придумаем любое имя. Сообщим его дочернему процессу через командную строку.
                    Дочерний процесс добавит к имени свой идентификатор в текстовом виде.
                    Чтобы для разных дочерних процессов имена не совпадали.
                    Но как его узнать родителю, чтобы создать объект синхронизации ?
                    Используем CREATE_SUSPENDED. Процесс создан, (но его поток ещё не запущен).
                    После этого ID дочернего у родителя есть.
                    Теперь родитель делает эвент и запускает поток дочернего процесса.
                    Ребёнок получает доступ к эвенту, засыпает на нём и ждёт команды...
                    -----
                    Ещё пример:
                    1. Пишу некую программу-лоадер, которая запустит другую программу.
                    2. С флагом CREATE_SUSPENDED.
                    3. Процесс в памяти, не запущен, дочерний.
                    4. Это значит, что я могу корректировать коды дочернего процесса как захочу.
                    5. Программа-лоадер производит коррекции кодов дочернего процесса и запускает его.
                    6. После коррекции дочерний процесс ведёт себя иначе, чем планировал разработчик..
                    Сообщение отредактировано: ЫукпШ -
                      Как-то неубедительно. Всё это скорее "о! я придумал, где это можно применить", чем "о да, это клёвый сценарий". ID к примеру, легко заменяются инкрементируемыми для каждого потомка int-ами. Патчить код проще, прикинувшись отладчиком, всё равно и для того, и для другого нужны высокие привилегии.
                      Я вот тоже чуть не написал, что если нужно создать мульён потоков, но застартить их единомоментно, то CREATE_SUSPENDED подойдёт. Нет, не подойдёт, будить придётся всё равно в цикле. А правильно – сделать эвент "Go!Go!Go!", нехай после старта ждут, а опосля взвести его одной функцией.

                      Добавлено
                      И выходом командовать глобальным булевым признаком как-то некрасиво. Делаем опять же эвент, нехай его ждут WaitForMultiple..., всё равно нитки ждут операционных событий, от ещё одного эвента не убудет, и в нужный момент Set ему.
                      Сообщение отредактировано: Qraizer -
                        Цитата Qraizer @
                        И выходом командовать глобальным булевым признаком как-то некрасиво.
                        Ну а что плохого? То, что глобальная? А если в класс завернуть? :)
                        Но в целом решение с Multiple'ом нормальное :)
                          Цитата Jin X @
                          Можно без статусов, но решил сделать так.
                          Чуть меньше накладных расходов (на вызов WaitForSingleObject) + лишняя подстраховка на случай ошибки создания ивента (хотя я понимаю, что это кране маловероятная ситуация).
                          Код получился больше и сложнее, но мне так нравится.

                          Что-то не вижу у тебя этой подстраховки. Да и накладных расходов у тебя там получается на порядок больше. Думаешь atomic-операции они бесплатные?

                          Добавлено
                          Цитата ЫукпШ @
                          Поскольку такая возможность существует, значит у разработчиков были основания.


                          Думаю, основание там было одно - раз это сделать легко, значит надо сделать, авось потом кто-нибудь придумает зачем.

                          Цитата ЫукпШ @
                          Теперь родитель делает эвент и запускает поток дочернего процесса.


                          А как запустить поток в другом процессе?
                            Цитата Олег М @
                            А как запустить поток в другом процессе?

                            Всё так же.
                            Если я создаю дочерний процесс (с флагом CREATE_SUSPENDED),
                            то я могу запустить главный поток посредством "ResumeThread".
                            Поскольку хэндл потока у меня будет после создания.
                            ---
                            Останавливать/запускать можно любой поток, надо иметь хэндл ("рукоятка, чтобы порулить")
                            и права доступа.
                            По отношению к дочернему процессу имеются достаточные права доступа,
                            с ним можно сделать практически всё-что-угодно.
                            ---
                            Если ты хочешь "RemoteThread", то это сложнее. В Сети есть хорошие статьи на эту тему.
                            Сообщение отредактировано: ЫукпШ -
                              Цитата Олег М @
                              Что-то не вижу у тебя этой подстраховки.
                              Например:
                              ExpandedWrap disabled
                                      if (ioThreads.hEvent[(int)lpParam]) {
                                        WaitForSingleObject(ioThreads.hEvent[(int)lpParam], INFINITE);
                                      } else {
                                        Sleep(1);
                                      }

                              Цитата Олег М @
                              Да и накладных расходов у тебя там получается на порядок больше. Думаешь atomic-операции они бесплатные?
                              А где ты видишь в цикле atomic-операции?

                              Добавлено
                              А избавиться от atomic, если уж на то пошло, можно разделив стоп-флаг и флаг паузы на 2 части.
                              Но тут особого смысла нет, т.к. пауза используется редко.
                              Хотя было бы чуть проще в реализации, конечно.
                                Цитата Jin X @
                                Например:

                                      if (ioThreads.hEvent[(int)lpParam]) {
                                        WaitForSingleObject(ioThreads.hEvent[(int)lpParam], INFINITE);
                                      } else {


                                Здесь лучше проверять, что возвращает WaitForSingleObject. А ещё лучше, если CreateEvent вернул ошибку, вообще не запускать поток.
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0488 ]   [ 17 queries used ]   [ Generated: 29.03.24, 10:08 GMT ]