Event


Event

A worker thread can be temporarily suspended because it does not have any work to do. The main thread can use an event to wake up any worker thread (that has been suspended) so that the worker thread can complete some pending work. When a worker thread is not longer necessary, an event can be used to wake up the worker thread so that it can terminate (or kill) itself.
Una thread de trabajo puede estar temporalmente pausada porque ésta no tiene trabajo por hacer. La thread principal puede usar un evento para despertar cualquier thread (que esta pausada) de tal forma que la thread de trabajo pueda completar algún trabajo pendiente. Cuando una thread de trabajo ya no es necesaria, un evento puede ser usado para despertar la thread de trabajo de tal forma que esta pueda terminar (o matarse así misma).

Tip
The main advantage of suspend a thread instead of terminating it is that it is not necessary to create a new thread every time it is needed. An event is necessary to wake up threads that are suspended.
La principal ventaja de pausar una thread en lugar de terminarla es que no es necesario crear una nueva thread cada vez que es necesaria. Un evento es necesario para despertar una thread que esta pausada.

::WaitForSingleObject and ::WaitForMultipleObjects

Both functions can be used to suspend a thread until an object (in the following example an event) is signaled. These functions are used to wait for completion of results by other threads without wasting CPU time.
Ambas funciones pueden ser usadas para suspender una thread hasta que un objeto (en el siguiente ejemplo un evento) es señalizado. Estas funciones son usadas para esperar por el completado de resultados por otras threads sin gastar tiempo de CPU.

Problem 1
Create a Wintempla Dialog application called PiRecycle to estimate the value of pi using a series of Taylor. Edit the GUI as shown using Wintempla. Before closing Wintempla, double click anywhere in the GUI to open the Properties Dialog of the main dialog, in the Event tab: check the App event (A custom application event) and Close (When the window should terminate)
Cree una aplicación de Dialogo usando Wintempla llamada PiRecycle para estimar el valor de pi usando una serie de Taylor. Edite la interface gráfica como se muestra usando Wintempla. Antes de cerrar Wintempla, hace doble clic en cualquier parte en la GUI para abrir el Diálogo de Propiedades del diálogo principal, en la Pestaña de Eventos: marque el evento App (A custom application event) y Close (When the window should terminate).

Event1

PiRecycleRun

PiRecycle.h
#pragma once //______________________________________ PiRecycle.h
#include "Resource.h"
#define WORK_ID 100

class PiRecycle: public Win::Dialog
{
public:
     PiRecycle()
     {
          termCount = 0;
          estimatePi = 0;
          eventRequest = ::CreateEvent(NULL, FALSE, FALSE, NULL);
          eventCompletion = ::CreateEvent(NULL, FALSE, FALSE, NULL);
          ::InitializeCriticalSection(&cs);
          terminate = false;
     }
     ~PiRecycle()
     {
          if (eventRequest) ::CloseHandle(eventRequest);
          if (eventCompletion) ::CloseHandle(eventCompletion);
          ::DeleteCriticalSection(&cs);
     }
     CRITICAL_SECTION cs;
     bool terminate;
     HANDLE eventRequest;
     HANDLE eventCompletion;
     static unsigned WINAPI ThreadFunc(LPVOID param);
     HANDLE hThread;
     //
     unsigned int termCount; // As we do not use this variable at the same time in both threads it is not necessary a Mt::IntTs variable
     double estimatePi; // As we do not use this variable at the same time in both threads it is not necessary a Mt::DoubleTs variable
protected:
     ...
};


PiRecycle.cpp
...
void PiRecycle::Window_Open(Win::Event& e)
{
     unsigned int threadId;
     hThread = (HANDLE)::_beginthreadex(NULL, 0, ThreadFunc, (LPVOID)this, 0, &threadId);
}

void PiRecycle::btRun_Click(Win::Event& e)
{
     tbxResult.Text = L" * * *";
     btRun.Enabled = false;
     termCount = tbxTermCount.IntValue;
     ::SetEvent(eventRequest); // Wake up the thread so that it can compute the value of PI
}

void PiRecycle::Window_Close(Win::Event& e)
{
     ::EnterCriticalSection(&cs);
     terminate = true;
     ::LeaveCriticalSection(&cs);
     //
     ::SetEvent(eventRequest); // Wake up the thread so that it can terminate itself
     ::WaitForSingleObject(hThread, INFINITE);
     ::CloseHandle(hThread);
     this->Destroy(); // Use this to close and destroy the Window
}

unsigned WINAPI PiRecycle::ThreadFunc(LPVOID param)
{
     PiRecycle* pir = (PiRecycle*)param;

     while(true)
     {
          ::WaitForSingleObject(pir->eventRequest, INFINITE);// Suspend the thread
          //
          ::EnterCriticalSection(&pir->cs);
          if (pir->terminate == true)
          {
               ::LeaveCriticalSection(&pir->cs);
               break; // Terminate the thread cleanly
          }
          ::LeaveCriticalSection(&pir->cs);
          //________________________________________________________________ Compute PI
          double sum = 0.0;
          for (unsigned int i =0; i<pir->termCount; i++)
          {
               if (i%2 == 0)
                    sum += (1.0/(2*i+1));
               else
                    sum -= (1.0/(2*i+1));
          }
          pir->estimatePi = 4.0*sum;
          ::PostMessage(pir->hWnd, WM_APP, (WPARAM)0, (LPARAM)WORK_ID); // Notify the main thread that we are done!
          ::SetEvent(pir->eventCompletion);
      }
     return 0;
}

void PiRecycle::Window_App(Win::Event& e)
{
     if (e.lParam == WORK_ID)
     {
          ::WaitForSingleObject(eventCompletion, INFINITE);
          //______________________________________________
          btRun.Enabled = true;
          //______________________________________________ Display result
          wchar_t text[256];
          _snwprintf_s(text, 256, _TRUNCATE, L"%.15f", estimatePi);
          tbxResult.Text = text;
     }
}


Tip
Wintempla provides the Mt::SuspendedThread class to simplify the use of suspended threads. Always try creating classes to simplify your code. Remember that a class must be as generic as possible so that it can be used in other applications. Wintempla provides some classes to simplify the use of events, critical sections and safe-thread values.
Wintempla proporciona la clase Mt::SuspendedThread para simplificar el uso de threads suspendidas. Siempre trate de crear clases para simplificar su código. Recuerde que una clase debe ser tan genérica como se pueda de tal forma que ésta puede usarse en otras aplicaciones. Wintempla proporciona algunas clases para simplificar el uso de evento, secciones críticas y valores seguros en threads.

Problem 2
Create a project called PiRecycleX to simplify the previous program using Wintempla classes. Note that the Mt::SuspendedThread class can help you design your own classes for multithread programs. Edit the GUI as shown using Wintempla. Before closing Wintempla, double click anywhere in the GUI to open the Properties Dialog of the main dialog, in the Event tab: check the App event (A custom application event), Timer and Close (When the window should terminate)
Cree un proyecto llamado PiRecycleX para simplificar el programa anterior usando las clases de Wintempla. Observe que la clase Mt::SuspendedThread puede ayudarlo a diseñar sus propias clases para programas multihilo. Edite la interface gráfica como se muestra usando Wintempla. Antes de cerrar Wintempla, hace doble clic en cualquier parte en la GUI para abrir el Diálogo de Propiedades del diálogo principal, en la Pestaña de Eventos: marque el evento App (A custom application event), Timer y Close (When the window should terminate).FIGPGN XEvent1FIGPGN XEvent2

PiRecycleXRun

PiRecycleX.h
#pragma once //______________________________________ PiRecycleX.h
#include "Resource.h"
#define WORK_ID 100

class PiRecycleX: public Win::Dialog, public Mt::IThreadX
{
public:
     PiRecycleX()
     {
          termCount = 0;
          estimatePi = 0;
     }
     ~PiRecycleX()
     {
     }
     Mt::DoubleTs progress;
     Sys::LowResStopwatch stopwatch;
     //
     Mt::SuspendedThread suspendedThread;
     unsigned int termCount; // As we do not use this variable at the same time in both threads it is not necessary a Mt::IntTs variable
     double estimatePi; // As we do not use this variable at the same time in both threads it is not necessary a Mt::DoubleTs variable
     //__________________________________ Mt::IThreadX
     DWORD ThreadFunc(Mt::BoolTs& cancel, int threadIndex, int numThreads);
protected:
     ...
};


PiRecycleX.cpp
...
void PiRecycleX::Window_Open(Win::Event& e)
{
}

void PiRecycleX::btRun_Click(Win::Event& e)
{
     this->timer.Set(1, 1000);
     stopwatch.Start();
     tbxResult.Text = L" * * *";
     btRun.Enabled = false;
     termCount = tbxTermCount.IntValue;
     suspendedThread.WakeUpAndWork(*this);
}

DWORD PiRecycleX::ThreadFunc(Mt::BoolTs& cancel, int threadIndex, int numThreads)
{
     double sum = 0.0;
     for (unsigned int i =0; i<termCount; i++)
     {
          if (cancel == true) break;
          progress = (100.0*i)/termCount;
          if (i%2 == 0)
               sum += (1.0/(2*i+1));
          else
               sum -= (1.0/(2*i+1));
     }
     estimatePi = 4.0*sum;
     ::PostMessage(hWnd, WM_APP, (WPARAM)0, (LPARAM)WORK_ID); // Notify the main thread that we are done!
     return 0;
}

void PiRecycleX::Window_Close(Win::Event& e)
{
     if (suspendedThread.IsBusy())
     {
          this->MessageBox(L"Program is busy", L"PiRecycle", MB_OK | MB_ICONWARNING);
          return;
     }
     this->Destroy();
}

void PiRecycleX::Window_App(Win::Event& e)
{
     if (e.lParam == WORK_ID)
     {
          //______________________________________________ 1. Wait for worker thread
          suspendedThread.WaitForExit();
          btRun.Enabled = true;
          this->timer.Kill(1);
          //______________________________________________ 2. Display result
          wchar_t text[256];
          _snwprintf_s(text, 256, _TRUNCATE, L"%.15f", estimatePi);
          tbxResult.Text = text;
     }
}

void PiRecycleX::btCancel_Click(Win::Event& e)
{
     suspendedThread.cancel = true;
}

void PiRecycleX::Window_Timer(Win::Event& e)
{
     if (suspendedThread.IsBusy())
     {
          wchar_t text[256];
          stopwatch.GetProgressInfo(progress.Get(), text, 256);
          this->Text = text;
     }
     else
     {
          this->Text = L"Done";
     }
}


C++ 11 condition_variable

Starting in version 11, the language C++ supports for condition_variable. It defines the classes condition_variable and condition_variable_any that are used to create objects that wait for a condition to become true. Thus, these classes can be used to block a thread, or multiple threads at the same time, until another thread modifies a shared variable and notifies the condition_variable. The program below starts a worker thread which executes SomeFunc. As soon as the code inside SomeFunc stars, the worker thread is blocked.Then, the main thread uses a condition_variable to unblock the worker thread. Later in the code, the main thread is blocked until the worker thread unblock it.
A partir de la versión 11, el lenguaje C++ suporta: condition_variable. Este define las clases condition_variable y condition_variable_any que pueden usarse para crear objetos que esperan a que una condición sea verdadera (true). Así, estas clases pueden ser usadas para bloquear una thread, o varias threads al mismo tiempo, hasta que otra thread modifique una variable compartida y notifique la condition_variable. El programa de abajo inicia una worker thread la cual ejecute SomeFunc. Tan pronto como el código dentro de SomeFunc inicia, la worker thread es bloqueada. Entonces, la thread principal usa una condition_variable para desbloquear la worker thread. Después en el código, la thread principal se bloquea hasta que la worker thread la desbloquea.

Program.h
#pragma once //______________________________________ Program.h
#include "resource.h"
#include <thread>
#include <mutex>
#include <condition_variable>

class Program: public Win::Dialog
{
public:
     Program()
     {
     }
     ~Program()
     {
     }
     static bool beginWorking;
     static bool workIsFinished;
     static std::mutex mtx;
     static std::condition_variable conditionVariable;
     static void SomeFunc(int a, double b);
     ...
};
Program.cpp
...
bool Program::beginWorking;
bool Program::workIsFinished;
std::mutex Program::mtx;
std::condition_variable Program::conditionVariable;

void Program::Window_Open(Win::Event& e)
{
     beginWorking = false;
     workIsFinished = false;
     //______________________________________________ 1. Start a thread, the thread executes SomeFunc(4, 5.34);
     std::thread workerThread(SomeFunc, 4, 5.34);
     //______________________________________________ 2. Notify the workerThread and unblock it
     mtx.lock();
     beginWorking = true;
     mtx.unlock();
     conditionVariable.notify_one();
     //______________________________________________ 3. Wait for the workerThread to send notification
     {
          std::unique_lock<std::mutex> someLock(mtx);
          conditionVariable.wait(someLock, []{return workIsFinished;});
     }
     //______________________________________________ 4. Wait for worker to thread to complete
     workerThread.join();
}

void Program::SomeFunc(int a, double b)
{
     std::unique_lock<std::mutex> someLock(mtx);
     //______________________________________________ 1. Worker thread will block here
     conditionVariable.wait(someLock, []{return beginWorking;});
     // DO SOME WORK HERE
     // ...
     workIsFinished = true;
     //______________________________________________ 2. Notify main thread that workIsFinished
     someLock.unlock();
     conditionVariable.notify_one();
}


C++ 11 future

Starting in version 11, the language C++ supports the future sets of classes to simplify running a function (usually in a separate thread) and retrieve its results. To use these classes, you must include the header file future.
A partir de la versión 11, el lenguaje C++ suporta los conjuntos de clases future para simplificar ejecutar una función (usualmente en una thread adicional) y retraer sus resultados. Para usar estas clases usted debe incluir el archivo de encabezado future.

© Copyright 2000-2019 Wintempla selo. All Rights Reserved. Sep 05 2019. Home