Модераторы: feodorv, GremlinProg, xvr, Fixin

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> SetWaitableTimer(), как задать период срабатывания таймера 
V
    Опции темы
wallstreet
Дата 11.3.2012, 16:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 153
Регистрация: 11.8.2011

Репутация: нет
Всего: нет



Всем доброго дня.
Задача стоит раз в секунду одним потоком выделить область для данных, вторым заполнить, третим отобразить.
Период в секунду нужно отобразить скорее всего ожидаемым таймером.
Делаю вот так:
Код

static int **buf;
CRITICAL_SECTION cs;
int j;
TCHAR str[10];
HANDLE hTimer;
unsigned _stdcall Thread1(void *param)
{
    EnterCriticalSection(&cs);
    WaitForSingleObject(hTimer, INFINITE);
    buf = new int*[10]; // 10 строк в массиве
        for(int i=0; i<10; i++)
            buf[i] = new int[10]; // 10 столбцов в массиве
        LeaveCriticalSection(&cs);
    return 0;
}
unsigned _stdcall Thread2(void *param)
{
    // ЗАПОЛНЕНИЕ МАССИВА
    EnterCriticalSection(&cs);
    WaitForSingleObject(hTimer, INFINITE);
    for(int count_row=0; count_row<10; count_row++)
        for(int count_column=0; count_column<10; count_column++)
            buf[count_row][count_column] = j++;
    LeaveCriticalSection(&cs);
    return 0;
}
unsigned _stdcall Thread3(void *param)
{
    WaitForSingleObject(hTimer, INFINITE);
    // Перерисовка окна
    InvalidateRect((HWND)param, NULL, TRUE);
    return 0;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;
    int x = 0;
    int y = 0;
    LARGE_INTEGER lTime;  // устанавливаем период через который таймер освободит объект для потоков
    static HANDLE hThread1, hThread2, hThread3;

    switch (message)
    {
    case WM_CREATE:
        InitializeCriticalSection(&cs);
        lTime.QuadPart = -10000000;
        hTimer = CreateWaitableTimer(NULL, TRUE, NULL); //создаем ожидаемый таймер
        SetWaitableTimer(hTimer, &lTime, 1000, NULL, NULL, FALSE); // установим основные настройки таймера

        hThread1 = (HANDLE)_beginthreadex(NULL, 0, Thread1, hWnd, 0, NULL);
        hThread2 = (HANDLE)_beginthreadex(NULL, 0, Thread2, hWnd, 0, NULL);
        hThread3 = (HANDLE)_beginthreadex(NULL, 0, Thread3, hWnd, 0, NULL);
    break;
    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case IDM_ABOUT:
            DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
            break;
        case IDM_EXIT:
            for(int count=0; count<10; count++)
                delete []buf[count];
            DestroyWindow(hWnd);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        break;
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        // Вывод содержимого в окно
        EnterCriticalSection(&cs);
        for(int count_row=0; count_row<10; count_row++)
                for(int count_column=0; count_column<10; count_column++)
                {
                    y=count_row*20;
                    x=count_column*20;
                    _itot(buf[count_row][count_column], str, 10);
                    TextOut(hdc, x, y, str, _tcslen(str));
                }
        LeaveCriticalSection(&cs);
        EndPaint(hWnd, &ps);
        break;
    case WM_DESTROY:
        CloseHandle(hThread1);
        CloseHandle(hThread2);
        CloseHandle(hThread3);
        DeleteCriticalSection(&cs);
        CancelWaitableTimer(hTimer);
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}



В результате таблица один раз отрисовывается и все, хотя третий параметр функции SetWaitableTimer(...,...,1000,...,...,...) равен насколько я понимаю одной секунде. т.е. после первой отрисовки J = 99 и следующая должна начаться с этого значения.
Что делаю не так?


блин код как-то не так отображаться стал, поэтому нужную строку подсветил



Это сообщение отредактировал(а) GremlinProg - 15.3.2012, 07:13
PM MAIL   Вверх
Dem_max
Дата 11.3.2012, 16:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1780
Регистрация: 12.4.2007

Репутация: 16
Всего: 39



Код

void __fastcall TForm1::Timer() 
{ 
int iDay; 
TDateTime DTime; 
FILETIME Local, UTC; 
SYSTEMTIME st, stmp; 
LARGE_INTEGER li; 
char *S = "USSSSSSS"; 

GetLocalTime(&stmp); 
HANDLE hTimer = CreateWaitableTimer(NULL, FALSE, NULL); 
DateTimeToSystemTime(DateTimePicker1->Time, st); 
if(stmp.wHour < st.wHour) 
      { 
        stmp.wHour = st.wHour; 
        stmp.wMinute = st.wMinute; 
      } 
else if((stmp.wHour == st.wHour)&&(stmp.wMinute < st.wMinute)) 
        stmp.wMinute = st.wMinute; 
else return; 

SystemTimeToFileTime(&stmp, &Local); 
LocalFileTimeToFileTime(&Local, &UTC); 
li.LowPart = UTC.dwLowDateTime; 
li.HighPart = UTC.dwHighDateTime; 
SetWaitableTimer(hTimer,&li,0,Syn,S,FALSE); 
SleepEx(INFINITE,true); 

}




--------------------
Американские программисты долго не могли понять, почему русские при зависании Windоws всё время повторяют "Твой зайка написал" ("Yоur bunnу wrоte")
PM MAIL   Вверх
wallstreet
Дата 11.3.2012, 21:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 153
Регистрация: 11.8.2011

Репутация: нет
Всего: нет



а вот так можно устанавливать время постоянного срабатывания с интервалом 1 сек:
Код

 lTime.QuadPart = -10000000;
 hTimer = CreateWaitableTimer(NULL, TRUE, NULL); 
 SetWaitableTimer(hTimer, &lTime, 1000, NULL, NULL, FALSE); 


или обязательно создавать переменную системного времени и приводить их к FILETIME
а потом уже использовать???? Думаю что нет.. Разве что если нам надо что бы таймер 
сработал в определенное время, тогда да. А в данном случае мой таймер освобождается
для потоков через 10 сек после создания и так через 1 сек в дальнейшем должно быть, но не получается

Это сообщение отредактировал(а) wallstreet - 11.3.2012, 21:08
PM MAIL   Вверх
feodorv
Дата 11.3.2012, 23:08 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 9
Всего: 45



Цитата(wallstreet @  11.3.2012,  17:19 Найти цитируемый пост)
раз в секунду

В коде этого нет. Потоки Thread1, Thread2 и Thread3 отработают один раз и завершатся.

Потом. По смыслу задачи, сначала отрабатывает первый поток с выделением памяти, уже потом должен отрабатывать второй поток с инициализацией, а, гм, третий (но есть ещё один наш, изначальный поток) должен заняться отрисовкой (в реальности он лишь посылает сообщение в оконную процедуру). Схема взаимодействия потоков в приведённом коде ужасная((( Её необходимо переделать.

Поскольку для отрисовки (в любой момент времени!) данных эти самые данные необходимо иметь, то, действительно, эти данные необходимо защитить от одновременного доступа. Критическая секция здесь подойдёт лучше всего, но не просто потому, что проще всего, а потому, что не имеет принадлежности к потоку, вызвавшему EnterCriticalSection (то есть освободить секцию мы можем в любом другом потоке!). Именно этим свойством я и предлагаю воспользоваться.

Но ещё раз скажу, что задача осложняется тем, что:
  • запрос на отрисовку может прийти в любой момент (у Вас это учтено критической секцией)
  • потоки должны пробуждаться в нужной последовательности (освобождение/выделение памяти -> инициализация -> отрисовка), но никак не в произвольном (у Вас это не учтено, так как порядок доступа к критической секции может отличаться от запланированного).
Поэтому предлагаю будить первый поток через таймер, второй поток - через событие2, третий - через событие3. Критическую секцию захватываем в первом потоке, а высвобождаем только в третьем! Ну, и отрисовку тоже защищаем критической секцией.

Таким образом получается:

Первый поток
Цикл по:
  • ожидание таймера или события1
  • проверяем, не нужно ли потоку заканчивать работу
  • захватываем критическую секцию
  • высвобождаем память, если она уже была заказана
  • заказываем память
  • устанавливаем событие2
  • идём на новый виток цикла
Второй поток
Цикл по:
  • ожидание события2
  • проверяем, не нужно ли потоку заканчивать работу
  • сбрасываем событие2
  • инициализируем выделенную память
  • устанавливаем событие3
  • идём на новый виток цикла
Третий поток
Цикл по:
  • ожидание события3
  • проверяем, не нужно ли потоку заканчивать работу
  • сбрасываем событие3
  • высвобождаем критическую секцию
  • вызываем InvalidateRect
  • идём на новый виток цикла
Таким образом мы гарантируем правильную очерёдность выполнения потоков и одновременно не допущения к данным памяти процедуры отрисовки.

Однако даже в таком варианте схема имеет изъян. Он связан с тем, что за время между срабатываниями таймера второй поток может не успеть проинициализировать данные, и при следующем срабатывании таймера первый поток начнёт перезаказывать память, а второй будет всё ещё инициализировать выделенную в прошлый раз память. Такая ситуация лечится тем, что придётся ввести вторую критическую секцию, которой обычным образом дополнительно защищаются данные в первом и втором потоках, ну с этим Вы справитесь самостоятельно...

ЗЫ События 1, 2 и 3 также используются для корректного завершения приложения.

Это сообщение отредактировал(а) feodorv - 11.3.2012, 23:17


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
wallstreet
Дата 12.3.2012, 18:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 153
Регистрация: 11.8.2011

Репутация: нет
Всего: нет



Огромное спасибо всем за участие.
Цитата

Цитата(wallstreet @  11.3.2012,  17:19 )


раз в секунду


В коде этого нет. Потоки Thread1, Thread2 и Thread3 отработают один раз и завершатся.
 


Надо сделать. В этом собственно и загвоздка. Из определения:
ОЖИДАЕМЫЕ ТАЙМЕРЫ - объекты ядра, которые самосоятельно переходят в свободное состояние в определенное время или через 
регулярные промежутки времени.

Вот мне этот промежуток и не удается задать((

Ну а теперь по порядку.
Цитата

■ожидание таймера или события1

А кто событие для первого потока будет освобождать? Предполагаю что поток 3й или раздел отрисовки окна WM_PAINT?


Цитата

■проверяем, не нужно ли потоку заканчивать работу

Насколько я понял, чо проверять должны на выполнение условия, т.е. применительно к потоку1, память под массив выделена, значит освобождаем событие1 переходим во второй поток который завершает первый или как?

Вот что получилось:
Код

static int **buf;
CRITICAL_SECTION cs1, cs2;
int j;
TCHAR str[10];
HANDLE hTimer;
static HANDLE hEvent1, hEvent2;

unsigned _stdcall Thread1(void *param)
{
    EnterCriticalSection(&cs1);            // Захватываем критическую секцию 1
    WaitForSingleObject(hTimer, INFINITE); // Ждем свободного состояния таймера
    EnterCriticalSection(&cs2);            // Захватываем критическую секцию 2
    if(!buf)                               // Проверяем массив на заполненние
    {
    // СОЗДАНИЕ НОВОГО МАССИВА
    buf = new int*[10];                    // 10 строк в массиве
        for(int i=0; i<10; i++)
            buf[i] = new int[10];          // 10 столбцов в массиве
    }
    else                                   // если память выделена на массив, заполняем нулями
    {
        for(int count_row=0; count_row<10; count_row++)
            for(int count_column=0; count_column<10; count_column++)
                buf[count_row][count_column] = 0;
    }
    SetEvent(hEvent1);                     // Освобождаем событие 1
    LeaveCriticalSection(&cs2);            // Освобождаем критическую секцию 2
    return 0;
}
unsigned _stdcall Thread2(void *param)
{
    WaitForSingleObject(hEvent1, INFINITE); // Ждем перехода события 1 в свободное состояние
    EnterCriticalSection(&cs2);             // Захватываем критическую секцию 2
    // ЗАПОЛНЕНИЕ МАССИВА
    for(int count_row=0; count_row<10; count_row++)
        for(int count_column=0; count_column<10; count_column++)
            buf[count_row][count_column] = j++;
    SetEvent(hEvent2);                      // Освобождаем событие 2
    LeaveCriticalSection(&cs2);             // Освобождаем критическую секцию 2
    return 0;
}
unsigned _stdcall Thread3(void *param)
{
    WaitForSingleObject(hEvent2, INFINITE); // Ждем перехода события 2 в свободное состояние
    EnterCriticalSection(&cs2);             // Захватываем критическую секцию 2
    // СООБЩЕНИЕ НА ПЕРЕРИСОВКУ ОКНА
    InvalidateRect((HWND)param, NULL, TRUE);
    EnterCriticalSection(&cs2);             // Освобождаем критическую секцию 2
    LeaveCriticalSection(&cs1);             // Освобождаем критическую секцию 1
    return 0;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;
    int x = 0;
    int y = 0;
    LARGE_INTEGER lTime;                   // устанавливаем период через который таймер освободит объект для потоков
    static HANDLE hThread1, hThread2, hThread3;

    switch (message)
    {
    case WM_CREATE:
        InitializeCriticalSection(&cs1);  // Инициализируем критическую секцию 1
        InitializeCriticalSection(&cs2);  // Инициализируем критическую секцию 2
        hEvent1 = CreateEvent(NULL, FALSE, FALSE, NULL); // Создаем событие 1
        hEvent2 = CreateEvent(NULL, FALSE, FALSE, NULL); // Создаем событие 2
        lTime.QuadPart = -10000000;                      // Задаем время первого срабатывания таймера после создания
        hTimer = CreateWaitableTimer(NULL, FALSE, NULL); //создаем ожидаемый таймер
        SetWaitableTimer(hTimer, &lTime, 1000, NULL, NULL, FALSE); // установим основные настройки в таймер
        hThread1 = (HANDLE)_beginthreadex(NULL, 0, Thread1, hWnd, 0, NULL);
        hThread2 = (HANDLE)_beginthreadex(NULL, 0, Thread2, hWnd, 0, NULL);
        hThread3 = (HANDLE)_beginthreadex(NULL, 0, Thread3, hWnd, 0, NULL);
    break;
    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case IDM_ABOUT:
            DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
            break;
        case IDM_EXIT:
            for(int count=0; count<10; count++)
                delete []buf[count];
            DestroyWindow(hWnd);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        break;
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        // Вывод содержимого в окно
        EnterCriticalSection(&cs1);           // Захватываем критическую секцию 1
        for(int count_row=0; count_row<10; count_row++)
                for(int count_column=0; count_column<10; count_column++)
                {
                    y=count_row*20;
                    x=count_column*20;
                    _itot(buf[count_row][count_column], str, 10);
                    TextOut(hdc, x, y, str, _tcslen(str));
                }
        LeaveCriticalSection(&cs1);           // Освобождаем критическую секцию 1
        EndPaint(hWnd, &ps);
        break;
    case WM_DESTROY:
        CloseHandle(hThread1);
        CloseHandle(hThread2);
        CloseHandle(hThread3);
        CloseHandle(hEvent1);
        CloseHandle(hEvent2);
        DeleteCriticalSection(&cs1);
        DeleteCriticalSection(&cs2);
        CancelWaitableTimer(hTimer);
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}



Вообщем, так, к слову, в задаче нужно было обойтись исключительно критическими секциями. Но для меня важно уметь использовать все средства взаимодействия, поэтому благодарю за саппорт в обучении и надеюсь поможете разобраться с таймером его периодичностью и другими вопросами.

Это сообщение отредактировал(а) wallstreet - 12.3.2012, 18:32
PM MAIL   Вверх
feodorv
Дата 12.3.2012, 23:36 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 9
Всего: 45



Было бы проще, если бы в доступности находился весь проект)))
Несколько замечаний:

1/ 
Цитата(wallstreet @  12.3.2012,  19:30 Найти цитируемый пост)
       hEvent1 = CreateEvent(NULL, FALSE, FALSE, NULL); // Создаем событие 1
        hEvent2 = CreateEvent(NULL, FALSE, FALSE, NULL); // Создаем событие 2

Если мы создаём события со сбросом вручную, то 
Код

       hEvent1 = CreateEvent(NULL, TRUE, FALSE, NULL); // Создаем событие 1
        hEvent2 = CreateEvent(NULL, TRUE, FALSE, NULL); // Создаем событие 2

Правда, тогда нужны соответствующие ResetEvent в нужных местах...


2/
Цитата(wallstreet @  12.3.2012,  19:30 Найти цитируемый пост)
    EnterCriticalSection(&cs1);            // Захватываем критическую секцию 1
    WaitForSingleObject(hTimer, INFINITE); // Ждем свободного состояния таймера
    EnterCriticalSection(&cs2);            // Захватываем критическую секцию 2

Всё-таки мы сначала ждём таймера, а уже потом захватываем cs1... Этим мы даём возможность окну отрисовать клиенскую область при разных событиях (схлопывание-раскрытие окна, перекрытие окна, завершение работы...):
Код

    WaitForSingleObject(hTimer, INFINITE); // Ждем свободного состояния таймера
    EnterCriticalSection(&cs1);            // Захватываем критическую секцию 1
    EnterCriticalSection(&cs2);            // Захватываем критическую секцию 2

Ну и устанавливать события можно вне сферы деятельности критической секции...


3/ 
Цитата(feodorv @  12.3.2012,  00:08 Найти цитируемый пост)
проверяем, не нужно ли потоку заканчивать работу

Имелось ввиду, что если потоки будут бесперерывно работать, то их нужно корректно завершить при завершении всего приложения (при IDM_EXIT).  Обычно это делается так: заводится глобальная булева переменная (назовём её done), которая принимает значение TRUE только при необходимости завершить работу, а все созданные потоки пробуждаются тем или иным способом. Но поскольку Thread1 висит исключительно на таймере, то для завершения потока нужно дожидаться срабатываения таймера (для 1 секунды это терпимо, а если нужно было раз в минуту?) Для этого и вводилось соответствующее событие в Thread1, которое сбрасывалось бы только при IDM_EXIT.


4/ 
Цитата(feodorv @  12.3.2012,  00:08 Найти цитируемый пост)
Цикл по:

Вот это отсутствует smile Из-за этого
Цитата(feodorv @  12.3.2012,  00:08 Найти цитируемый пост)
Потоки Thread1, Thread2 и Thread3 отработают один раз и завершатся.

Иными словами, нужно так:
Код

BOOL done = FALSE;

unsigned _stdcall Thread1(void *param)
{
  HANDLE list[2];

  list[0] = hEvent0;
  list[1] = hTimer;
 
  while( !done )
  {
    WaitForMultipleObjects( 2, list, FALSE, INFINITE); // Ждем свободного состояния таймера
    if( done ) break;
    EnterCriticalSection(&cs1);            // Захватываем критическую секцию 1
    EnterCriticalSection(&cs2);            // Захватываем критическую секцию 2
    ...
    LeaveCriticalSection(&cs2);            // Освобождаем критическую секцию 2
    SetEvent(hEvent1);                     // Освобождаем событие 1
  }
  return 0;
}

Аналогично и в других потоках!!!


4/ Завершение приложения - отдельная песня. Ибо необходимо дождаться завершения всех трёх потоков через WaitForMultipleObjects, предварительно установив done в TRUE и пробудив потоки через SetEvent. Ждать можно максимум 10 секунд...
Код

done = TRUE;
SetEvent( hEvent0 );
SetEvent( hEvent1 );
SetEvent( hEvent2 );

HANDLE list[3];
list[0] = hThread1;
list[1] = hThread2;
list[2] = hThread3;
WaitForMultipleObjects( 3, list, TRUE, 10000); // Ждём завершения всех трёх потоков

И уже потом DestroyWindow()...


5/ при отрисовке нужно учитывать, что buf может быть NULL (например, если отрисовка произойдёт быстрее, чем первый поток успеет захватить критическую секцию), тогда просто можно ничего не рисовать. Гм, нужно также проследить, что Вы очищаете клиенскую область окна при перерисовке...


Цитата(wallstreet @  12.3.2012,  19:30 Найти цитируемый пост)
А кто событие для первого потока будет освобождать? 

Мы будем ждать или-или (а не и-и), поэтому если сработает таймер, то и поток1 пробудится. Про событие и его роль уже написал  smile 


Цитата(wallstreet @  12.3.2012,  19:30 Найти цитируемый пост)
Вот мне этот промежуток и не удается задать((

Во-первых, потоки отрабытывают всего один раз, сколько бы раз там таймер не срабатывал... Во-вторых, зачем эта задержка:
Цитата(wallstreet @  12.3.2012,  19:30 Найти цитируемый пост)
lTime.QuadPart = -10000000;                      // Задаем время первого срабатывания таймера после создания

Почему не просто 0? А так, на вид, всё правильно)))
В сущности, можно было бы начать писать программу с одного потока, сидящего на таймере, а два других добавить позже, когда таймер заработает...


Цитата(wallstreet @  12.3.2012,  19:30 Найти цитируемый пост)
Вообщем, так, к слову, в задаче нужно было обойтись исключительно критическими секциями.

Ну, эээ, теоретически, можно всё переписать исключительно через критические секции, но это будет сильно напряжно)))


Так что потихоньку двигаемся к финишу)))

Это сообщение отредактировал(а) feodorv - 12.3.2012, 23:48


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
wallstreet
Дата 13.3.2012, 13:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 153
Регистрация: 11.8.2011

Репутация: нет
Всего: нет



Ну вроде подогнал под ваши рекомендации.
По пунктам:

1\ 
А зачем нам создавать события с ручным сбросом. Мы дождались освобождения события и оно автоматом занимается потоком дождавшимся. Т.о. мы создаем событие занятым изначально, потом первый поток его освобождает для второго, а второй дождавшись занимает ну и тд. 

2\ 
реализовал по примеру  4\ 

3\ 
реализовал в IDM_EXIT после чего при попытке выхода путем Menu\Exit прога висит секунд 5, потом закрывает окно.  Однако для меня немного непонятным остается, что значит:
Цитата

Но поскольку Thread1 висит исключительно на таймере, то для завершения потока нужно дожидаться срабатываения таймера (для 1 секунды это терпимо, а если нужно было раз в минуту?)


4\ 
В целом не понимаю зачем завершать поток при выходе? Он ведь в рамках процесса окна создается, насколько я понимаю, соответственно и завершится по завершению процесса окна при выходе. Ну и путаница в голове по поводу освобождения событий возникает. Допустим осбытие0 освободилось, но при проверке done на TRUE нас сразу выкинет из потока но не завершит. Ведь насколько я понимаю завершение потока происходит с помощью функции _endthreadex(). Т.о. почему бы просто не взять и не завершить этой функцией все потоки по очереди в IDM_EXIT? Вобщем хотелось бы поподробнее о выходе.

5\
Вроде все ок.
Цитата

lTime.QuadPart = -10000000;

для того что бы визуально видеть что таймер срабатывает. А так можно и 0

Кстати что делать с отрисовкой раз в секунду моих данных?

Архив с проектом

Код

static int **buf;                             // Буфер для данных
CRITICAL_SECTION cs1, cs2;                    // Критические секции
int j; 
TCHAR str[10];                                // Строка для отрисовки
HANDLE hTimer;                                // Хендл таймера
static HANDLE hEvent0, hEvent1, hEvent2;      // Хендлы событий
BOOL done = FALSE;

unsigned _stdcall Thread1(void *param)
{
    HANDLE list[2];
    list[0] = hEvent0;
    list[1] = hTimer;

    while(!done)
    {
        WaitForMultipleObjects(2, list, FALSE, INFINITE); // Ждем свободного состояния таймера или освобождения события 0
        if(done)
            break;
        EnterCriticalSection(&cs1);            // Захватываем критическую секцию 1
        EnterCriticalSection(&cs2);            // Захватываем критическую секцию 2
        if(!buf)                               // Проверяем массив на заполненние
        {
        // СОЗДАНИЕ НОВОГО МАССИВА
        buf = new int*[10];                    // 10 строк в массиве
            for(int i=0; i<10; i++)
                buf[i] = new int[10];          // 10 столбцов в массиве
        }
        else                                   // если память выделена на массив, заполняем нулями
        {
            for(int count_row=0; count_row<10; count_row++)
                for(int count_column=0; count_column<10; count_column++)
                    buf[count_row][count_column] = 0;
        }
        SetEvent(hEvent1);                     // Освобождаем событие 1
        LeaveCriticalSection(&cs2);            // Освобождаем критическую секцию 2
    }
    return 0;
}
unsigned _stdcall Thread2(void *param)
{
    while(!done)
    {
        WaitForSingleObject(hEvent1, INFINITE); // Ждем перехода события 1 в свободное состояние
        if(done)
            break;
        EnterCriticalSection(&cs2);             // Захватываем критическую секцию 2
        // ЗАПОЛНЕНИЕ МАССИВА
        for(int count_row=0; count_row<10; count_row++)
            for(int count_column=0; count_column<10; count_column++)
                buf[count_row][count_column] = j++;
        SetEvent(hEvent2);                      // Освобождаем событие 2
        LeaveCriticalSection(&cs2);             // Освобождаем критическую секцию 2
    }
    return 0;
}
unsigned _stdcall Thread3(void *param)
{
    while(!done)
    {
        WaitForSingleObject(hEvent2, INFINITE); // Ждем перехода события 2 в свободное состояние
        if(done)
            break;
        EnterCriticalSection(&cs2);             // Захватываем критическую секцию 2
        // СООБЩЕНИЕ НА ПЕРЕРИСОВКУ ОКНА
        InvalidateRect((HWND)param, NULL, TRUE);
        EnterCriticalSection(&cs2);             // Освобождаем критическую секцию 2
        LeaveCriticalSection(&cs1);             // Освобождаем критическую секцию 1
    }
    return 0;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;
    int x = 0;
    int y = 0;
    LARGE_INTEGER lTime;                   // устанавливаем период через который таймер освободит объект для потоков
    static HANDLE hThread1, hThread2, hThread3;

    switch (message)
    {
    case WM_CREATE:
        InitializeCriticalSection(&cs1);  // Инициализируем критическую секцию 1
        InitializeCriticalSection(&cs2);  // Инициализируем критическую секцию 2

        hEvent0 = CreateEvent(NULL, FALSE, FALSE, NULL); // Создаем событие 0
        hEvent1 = CreateEvent(NULL, FALSE, FALSE, NULL); // Создаем событие 1
        hEvent2 = CreateEvent(NULL, FALSE, FALSE, NULL); // Создаем событие 2

        lTime.QuadPart = -10000000;                      // Задаем время первого срабатывания таймера после создания
        hTimer = CreateWaitableTimer(NULL, FALSE, NULL); //создаем ожидаемый таймер
        SetWaitableTimer(hTimer, &lTime, 1000, NULL, NULL, FALSE); // установим основные настройки в таймер

        hThread1 = (HANDLE)_beginthreadex(NULL, 0, Thread1, hWnd, 0, NULL); // Создаем поток 1
        hThread2 = (HANDLE)_beginthreadex(NULL, 0, Thread2, hWnd, 0, NULL); // Создаем поток 2
        hThread3 = (HANDLE)_beginthreadex(NULL, 0, Thread3, hWnd, 0, NULL); // Создаем поток 3
    break;
    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case IDM_ABOUT:
            DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
            break;
        case IDM_EXIT:
            done = TRUE;
            SetEvent(hEvent0);
            SetEvent(hEvent1);
            SetEvent(hEvent2);
            HANDLE list[3];
            list[0] = hThread1;
            list[1] = hThread2;
            list[2] = hThread3;
            WaitForMultipleObjects(3, list, TRUE, 10000); // Ждем завершения всех потоков
            CloseHandle(hThread1);                        // Закрываем хендлы потоков и событий
            CloseHandle(hThread2);
            CloseHandle(hThread3);
            CloseHandle(hEvent0);
            CloseHandle(hEvent1);
            CloseHandle(hEvent2);
            DeleteCriticalSection(&cs1);                  // Удаляем критические секции
            DeleteCriticalSection(&cs2);
            CancelWaitableTimer(hTimer);                  // Останавливаем таймер
            for(int count=0; count<10; count++)           // Чистим память выделенную под буфер
                delete []buf[count];
            DestroyWindow(hWnd);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        break;
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        // ОТРИСОВКА ДАННЫХ В ОКНЕ
        if(buf)                                   // Отрисовываем данные, только если они есть
        {
            EnterCriticalSection(&cs1);           // Захватываем критическую секцию 1
            for(int count_row=0; count_row<10; count_row++)
                    for(int count_column=0; count_column<10; count_column++)
                    {
                        y=count_row*20;
                        x=count_column*20;
                        _itot(buf[count_row][count_column], str, 10);
                        TextOut(hdc, x, y, str, _tcslen(str));
                    }
            LeaveCriticalSection(&cs1);           // Освобождаем критическую секцию 1
        }
        EndPaint(hWnd, &ps);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}


Большое спасибо за помощь.

Это сообщение отредактировал(а) wallstreet - 13.3.2012, 14:00
PM MAIL   Вверх
feodorv
Дата 13.3.2012, 23:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 9
Всего: 45



Цитата(wallstreet @  13.3.2012,  14:55 Найти цитируемый пост)
Архив с проектом

Спасибо!
В конце концов удалось скомпилиться и запуститься))))


Цитата(wallstreet @  13.3.2012,  14:55 Найти цитируемый пост)
Кстати что делать с отрисовкой раз в секунду моих данных?

Просто у Вас ошибка в Thread3:
Цитата(wallstreet @  13.3.2012,  14:55 Найти цитируемый пост)
        EnterCriticalSection(&cs2);             // Захватываем критическую секцию 2
        // СООБЩЕНИЕ НА ПЕРЕРИСОВКУ ОКНА
        InvalidateRect((HWND)param, NULL, TRUE);
        EnterCriticalSection(&cs2);             // Освобождаем критическую секцию 2

Мало того, что там совсем не нужно защищать код критической секцией, так ещё и EnterCriticalSection(&cs2); два разА прописано (типичный копи-паст баг))))
СтОит только оставить:
Код

        InvalidateRect((HWND)param, NULL, TRUE);

И обновление идёт)))) Правда, как я уже говорил:
Цитата(feodorv @  13.3.2012,  00:36 Найти цитируемый пост)
Гм, нужно также проследить, что Вы очищаете клиенскую область окна при перерисовке...

Ну, это Вы уж как-нибудь сами)))


Цитата(wallstreet @  13.3.2012,  14:55 Найти цитируемый пост)
А зачем нам создавать события с ручным сбросом.

Ну, в данном случае не нужно))) Так как у нас один поток = одно событие. При другом раскладе стОит сделать выбор...


Цитата(wallstreet @  13.3.2012,  14:55 Найти цитируемый пост)
после чего при попытке выхода путем Menu\Exit прога висит секунд 5, потом закрывает окно. 

А у меня не висит ни секундочки (release) smile Это, правда, после вышеозначенного исправления)))


Цитата(wallstreet @  13.3.2012,  14:55 Найти цитируемый пост)
Однако для меня немного непонятным остается, что значит:
Цитата
Но поскольку Thread1 висит исключительно на таймере, то для завершения потока нужно дожидаться срабатываения таймера (для 1 секунды это терпимо, а если нужно было раз в минуту?)

В первоначальном варианте события hEvent0 не было, а без него поток пробуждался исключительно по таймеру. И сделав WaitForObject(s) (hThread1) мы бы зависли до тех пор, пока не сработал бы таймер, пробудив поток. Используя hEvent0, мы не зависим от таймера (при пробуждении первого потока).


Цитата(wallstreet @  13.3.2012,  14:55 Найти цитируемый пост)
Ведь насколько я понимаю завершение потока происходит с помощью функции _endthreadex().

Где?)))
_endthreadex() вызывается функцией _beginthreadex() по окончании выполнения функции потока. А Вы функцию потока завершать никак не хотели))) И мы бы остались с тремя потоками при завершении программы. Их бы всё равно прибила операционная система (при возврате из WinMain) посредством TerminateThread, но этого допускать не следует, так как при внезапной кончине одного потока другой поток своими действиями может вызвать нарушение доступа.


Цитата(wallstreet @  13.3.2012,  14:55 Найти цитируемый пост)
Т.о. почему бы просто не взять и не завершить этой функцией все потоки по очереди в IDM_EXIT?

Гм. Вот как раз по тем самым причинам.  Представьте себе, Вы говорите программе "Exit", а она: "Access violation..." Не комильфо)))
Самый лучший вариант - дать потоку завершиться самому.


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
wallstreet
Дата 14.3.2012, 11:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 153
Регистрация: 11.8.2011

Репутация: нет
Всего: нет



Оооо, да.
Ошибка зачетная была...
Ну теперь все работает как часы. 
Примного благодарствую)
PM MAIL   Вверх
GremlinProg
Дата 14.3.2012, 12:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2706
Регистрация: 9.8.2005
Где: Тюмень

Репутация: 99
Всего: 106



Цитата(feodorv @  12.3.2012,  01:08 Найти цитируемый пост)
Критическая секция здесь подойдёт лучше всего, но не просто потому, что проще всего, а потому, что не имеет принадлежности к потоку, вызвавшему EnterCriticalSection (то есть освободить секцию мы можем в любом другом потоке!). Именно этим свойством я и предлагаю воспользоваться.

вот EnterCriticalSection как раз имеет такую принадлежность,
а то, что LeaveCriticalSection может освободить чужую секцию можно расценивать как избыточную функциональность, которая может приводить к различным ошибкам
Цитата(LeaveCriticalSection Function @  MSDN)

If a thread calls LeaveCriticalSection when it does not have ownership of the specified critical section object, an error occurs that may cause another thread using EnterCriticalSection to wait indefinitely.

я бы не советовал пользоваться таким свойством
Цитата(wallstreet @  12.3.2012,  20:30 Найти цитируемый пост)
в задаче нужно было обойтись исключительно критическими секциями

судя по условию, задача учит просто синхронизировать доступ к массиву, управляемому разными потоками,
это не значит, что на секциях должно быть все
Цитата(feodorv @  12.3.2012,  01:08 Найти цитируемый пост)
По смыслу задачи, сначала отрабатывает первый поток с выделением памяти, уже потом должен отрабатывать второй поток с инициализацией, а, гм, третий (но есть ещё один наш, изначальный поток) должен заняться отрисовкой (в реальности он лишь посылает сообщение в оконную процедуру)

не надо пытаться думать о том, что какой-то поток дожен отработать раньше другого, это движение мыслей неправильное,
и приведет лишь к потерям времени, денег, нервов

если есть топологические отношения между потоками, значит они должны разрешаться синхронизаторами, по одному на каждое такое отношение, иначе стоит подумать о том, чтобы преобразовать такие потоки в один автомат
Цитата(wallstreet @  11.3.2012,  18:19 Найти цитируемый пост)
третим отобразить

отобразить, нарисовать - граница нечеткая,
но если нарисовать, то здесь основной поток вообще должен просто отобразить готовую картинку, которую третий поток как раз и формирует


--------------------
"Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины."
PM WWW ICQ   Вверх
feodorv
Дата 14.3.2012, 19:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 9
Всего: 45



Цитата(GremlinProg @  14.3.2012,  13:29 Найти цитируемый пост)
вот EnterCriticalSection как раз имеет такую принадлежность

Ок. Скажу иначе: потоку, захватившему критическую секцию, не передаются права владения ею (в отличии от мютекса). 


Цитата(GremlinProg @  14.3.2012,  13:29 Найти цитируемый пост)
If a thread calls LeaveCriticalSection when it does not have ownership of the specified critical section object, an error occurs that may cause another thread using EnterCriticalSection to wait indefinitely.

Может, но не обязательно приведёт.
С другой стороны, если "an error occurs", то схему лучше переделать. Хотя, честно говоря, не понимаю, какая именно возникает ошибка. То, что неправильное применение критических секций может вызвать взаимную блокировку, несомненно.


Цитата(GremlinProg @  14.3.2012,  13:29 Найти цитируемый пост)
не надо пытаться думать о том, что какой-то поток дожен отработать раньше другого, это движение мыслей неправильное,
и приведет лишь к потерям времени, денег, нервов

Интересное замечание)))
Но в данном случае это требуется по условию задачи.
Если будет такая возможность и желание, предложите свою схему, переделаем smile 


Цитата(wallstreet @  14.3.2012,  12:08 Найти цитируемый пост)
Примного благодарствую) 

На здоровье)))


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
feodorv
Дата 14.3.2012, 20:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 9
Всего: 45



Цитата(feodorv @  14.3.2012,  20:26 Найти цитируемый пост)
Хотя, честно говоря, не понимаю, какая именно возникает ошибка.

Кажется, всё же, понимаю)))
При отсутствии цикла в Thread1 всё было бы нормально. При наличии цикла возможно его зависание... Мдя)))

Это сообщение отредактировал(а) feodorv - 15.3.2012, 02:18


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
GremlinProg
Дата 15.3.2012, 06:55 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2706
Регистрация: 9.8.2005
Где: Тюмень

Репутация: 99
Всего: 106



Цитата(feodorv @  14.3.2012,  21:26 Найти цитируемый пост)
Ок. Скажу иначе: потоку, захватившему критическую секцию, не передаются права владения ею (в отличии от мютекса). 

угу
Цитата(feodorv @  14.3.2012,  21:26 Найти цитируемый пост)
То, что неправильное применение критических секций может вызвать взаимную блокировку, несомненно.

угу + включаем законы мерфи
Цитата(feodorv @  14.3.2012,  21:26 Найти цитируемый пост)
Но в данном случае это требуется по условию задачи.

по условию задачи, очередность отработки потоков не регламентируется )
Цитата(feodorv @  14.3.2012,  21:26 Найти цитируемый пост)
предложите свою схему

1. заводим секцию x, охраняющую модификацию/чтение массива
2. заводим событие (или семафор) y, разрешающий перезапись данных массива
3. заводим событие (или семафор) z, разрешающий отрисовку данных массива

4. заводим поток a, который ресайзит массив (с учетом входа в x) и сигнализирует y
5. заводим поток b, который при активации y заполняет массив (с учетом входа в x) и сигнализирует z
6. заводим поток c, который при активации z рисует массив во временный растр (с учетом входа в x) и посылает команду на обновление окна
7. в WM_PAINT просто отрисовать готовый растр, если он имеется

6 и 7 пункт, по хорошему, требует также синхронизации доступа к глобальному растру, решать это можно как секцией, так и простой взаимоблокировкой,

p.s.: если не понятно как реализовать синхронизацию доступа к глобальному растру взаимоблокировкой, могу потом показать

Добавлено через 8 минут и 10 секунд
+ для большей интерактивности,
на форме можно, разместить манипуляторы, управляющие первым потоком, например: для управления длиной массива и кнопкой, для "спуска" итерации первого потока, тогда потребуется еще одно событие, которого этот поток будет ожидать, перед ресайзом массива и, соответственно, которое будет сигнализировать кнопка


--------------------
"Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины."
PM WWW ICQ   Вверх
feodorv
Дата 15.3.2012, 16:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 9
Всего: 45



Цитата(GremlinProg @  15.3.2012,  07:55 Найти цитируемый пост)
+ включаем законы мерфи

Они всегда включены))) Но если жить только по ним, то лучше не жить)))


Цитата(GremlinProg @  15.3.2012,  07:55 Найти цитируемый пост)
по условию задачи, очередность отработки потоков не регламентируется )

Да, согласен. Просто есть желательная последовательность выполнения потоков (согласитесь, странно планировать программу так, чтобы пробуждались потоки в обратной последовательности, то есть сначала пробуждать поток с отрисовкой, потом пробуждать поток с инициализацией, и только потом с выделением памяти). Понятно, что реализовывать программу нужно исходя из того, что порядок выполнения и планирование потоков операцинной состемой может быть произвольными (с нашей, пользовательской, точки зрения). Это решает ОС.


Цитата(GremlinProg @  15.3.2012,  07:55 Найти цитируемый пост)
7. в WM_PAINT просто отрисовать готовый растр, если он имеется

Ок. 
А метафайл никак подойдёт под эту задачу?


Цитата(GremlinProg @  15.3.2012,  07:55 Найти цитируемый пост)
p.s.: если не понятно как реализовать синхронизацию доступа к глобальному растру взаимоблокировкой, могу потом показать

Не понятно, что здесь непонятного, поэтому лучше показать, чтобы рассеять все сомнения))))


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
GremlinProg
Дата 16.3.2012, 07:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2706
Регистрация: 9.8.2005
Где: Тюмень

Репутация: 99
Всего: 106



Цитата(feodorv @  15.3.2012,  18:03 Найти цитируемый пост)
Они всегда включены))) Но если жить только по ним, то лучше не жить)))

smile 
судя по твоему обширному плану, они выключены,

эти законы не для того, чтобы жить по ним,
а для того, чтобы не заморачиваться, и жить по своим smile
Цитата(feodorv @  15.3.2012,  18:03 Найти цитируемый пост)
согласитесь, странно планировать программу так, чтобы пробуждались потоки в обратной последовательности, то есть сначала пробуждать поток с отрисовкой, потом пробуждать поток с инициализацией, и только потом с выделением памяти

а процесс разработок - такая штука: чем дальше в лес, тем больше дров - не всегда может опираться на "желательную" логику работы программы,
особенно тогда, когда эта логика нестабильна и может быть легко нарушена


--------------------
"Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины."
PM WWW ICQ   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "C/C++: Системное программирование и WinAPI"
Fixin
GremlinProg
xvr
feodorv
  • Большое количество информации и примеров с использованием функций WinAPI можно найти в MSDN
  • Описание сообщений, уведомлений и примеров с использованием компонент WinAPI (BUTTON, EDIT, STATIC, и т.п.), можно найти в MSDN Control Library
  • Непосредственно, перед созданием новой темы, проверьте заголовок и удостоверьтесь, что он отражает суть обсуждения.
  • После заполнения поля "Название темы", обратите внимание на наличие и содержание панели "А здесь смотрели?", возможно Ваш вопрос уже был решен.
  • Приводите часть кода, в которой предположительно находится проблема или ошибка.
  • Если указываете код, пользуйтесь тегами [code][/code], или их кнопочными аналогами.
  • Если вопрос решен, воспользуйтесь соответствующей ссылкой, расположенной напротив названия темы.
  • Один топик - один вопрос!
  • Перед тем как создать тему - прочтите это .

На данный раздел распространяются Правила форума и Правила раздела С++:Общие вопросы .


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Chipset, Step, Fixin, GremlinProg, xvr. feodorv.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Системное программирование и WinAPI | Следующая тема »


 




[ Время генерации скрипта: 0.1101 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.