Critical Section


Critical Section

A critical section is an object to exchange information among threads. To create a critical section the program must call ::InitializeCriticalSection. When the program does not need the critical section, the program must call ::DeleteCriticalSection. There are three main functions of a critical section: ::EnterCriticalSection, ::LeaveCriticalSection, and ::TryEnterCriticalSection. See example below.
Una sección crítica es un objeto para intercambiar información entre threads. Para crear una sección crítica el programa debe llamar ::InitializeCriticalSection. Cuando el programa ya no necesita la sección crítica, el programa debe llamar ::DeleteCriticalSection. Hay tres funciones principales de una sección crítica: ::EnterCriticalSection, ::LeaveCriticalSection y ::TryEnterCriticalSection. Ver el ejemplo de abajo.

Program.h
class Program : public Win::Dialog
{
public:
     Program()
     {
          balance = 0.0;
          hThread = NULL;
          ::InitializeCriticalSection(&cs);
     }
     ~Program()
     {
          ::DeleteCriticalSection(&cs);
     }
     HANDLE hThread
     static unsigned WINAPI ThreadFunc(LPVOID param);
     double balance;
     CRITICAL_SECTION cs;
     ...
};


Program.cpp

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

void Program::btBuy_Click(Win::Event& e)
{
     ::EnterCriticalSection(&cs);
     balance = -10.1;
     ::LeaveCriticalSection(&cs);
}

void Program::btSell_Click(Win::Event& e)
{
     ::EnterCriticalSection(&cs);
     balance = 20.2;
     ::LeaveCriticalSection(&cs);
}

unsigned WINAPI Program::ThreadFunc(LPVOID param)
{
     Program* program = (Program*)param;
     ::EnterCriticalSection(&program->cs);
     program->balance -= 50.5;
     ::LeaveCriticalSection(&program->cs);
     ....
}


Critical Sections with Wintempla

In order to simplify multi-thread applications, Wintempla provides the class Mt::CriticalSection. A multi-thread program must have one CriticalSection (the cs variable in the code below) for each variable that will be used from more than one thread. Each thread must call cs.Enter() before using the variable. After using the variable, the thread must call cs.Leave() so that other threads can use the variables, see code below.
A fin de simplificar las aplicaciones multi-tarea, Wintempla proporciona la clase Mt::CriticalSection. Un programa multi-tarea debe tener una CriticalSection (la variable cs en el código de abajo) por cada variable que se usará desde más de una thread. Cada thread debe llamar cs.Enter() antes de usar la variable. Después de usar la variable, la thread debe llamar cs.Leave() para que otras threads puedan usar la variable, vea el código de abajo.

CriticalSection

Thread Safe Variables

A thread safe variable is a variable that can be used from several threads. Wintempla provides a set of classes to simplify the use of thread safe variables. These classes are: Mt::BoolTs, Mt::DoubleTs, Mt::IntTs, Mt::LongTs, Mt::StringTs and Mt::WstringTs. The code below illustrates how to use the Mt::DoubleTs class. These thread safe classes have a critical section to provide exclusive access to the variable, the funtions Set() and Get() of these classes hide the use of the critical section, making the use of Thread Safe Variables easy.
Una variable segura para threads es una variable que puede ser usada desde varias threads. Wintempla proporciona un conjunto de clases para simplificar el uso de variables seguras para threads. Estas clases son: Mt::BoolTs, Mt::DoubleTs, Mt::IntTs, Mt::LongTs, Mt::StringTs y Mt::WstringTs. El código de abajo ilustra cómo usar la clase Mt::DoubleTs. Estas clases seguras para threads tienen una sección crítica que proporciona el acceso exclusivo a la variable, las funciones Set() y Get() de estas clases esconden el uso de la sección crítica, haciendo el uso de Variables Seguras para Threads fácil.

Program.h
class Program : public Win::Dialog, public Mt::IThread
{
public:
     Program()
     {
          balance.Set(0.0);
     }
     ~Program()
     {
     }
     Mt::DoubleTs weight;
     Mt::ThreadObject threadObject;
     //____________________________________________________________ Mt::IThread
     DWORD ThreadFunc(Mt::BoolTs& cancel, Mt::DecimalTs& progress, Mt::BoolTs& resetTime);
     ...
};


Program.cpp

void Program::Window_Open(Win::Event& e)
{
     threadObject.StartThread(*this);
}

void Program::btBuy_Click(Win::Event& e)
{
     balance.Set(-10.1);
}

void Program::btSell_Click(Win::Event& e)
{
     balance.Set(20.2);
}

//____________________________________________________________ Mt::IThread
// cancel: you should return as quickly as possible from ThreadFunc when this variable is true
// progress: use this variable to report the progress of the thread (0.0 to 100.0)
// resetTime: use this variable when the thread has several steps and you want to report progress for each step.
// (if your thread does not have any steps, you can ignore this variable). Set "resetTime" to true, at the beginning of each step
DWORD Program::ThreadFunc(Mt::BoolTs& cancel, Mt::DecimalTs& progress, Mt::BoolTs& resetTime);
{
     balance.Set(balance.Get() - 50.5);
     ....
}


Enter (::EnterCriticalSection) and Leave (::LeaveCriticalSection)

When a thread wants to read or write one of more exchange variable, it must call the ::EnterCriticalSection API. Then, the thread can be read or write the values of one of more exchange variables. ::The EnterCriticalSection API ensures that only one thread is in a critical section. When the thread has read or written the exchange variable, it must call ::LeaveCritialSection.
Cuando una thread quiere leer o escribir una variable de intercambio, ésta debe llamar la API ::EnterCriticalSection. Entonces, la thread puede leer o escribir los valores de una o más variables de intercambio. La API ::EnterCriticalSection asegura que solamente una thread se encuentra en la sección crítica. Cuando una thread ha leído o escrito las variables de intercambio, ésta debe llamar ::LeaveCriticalSection

Tip
If one thread tries to enter a critical section that other thread has entered, this thread will be stop until the critical section becomes available. Thus, a critical section must have very few lines and must try to exchange the minimum amount of information among threads.
Si una thread trata de entrar una sección crítica que otra thread ha entrado, esta thread se detendrá hasta que la sección crítica esté disponible. Así, una sección crítica debe tener muy pocas líneas y debe tratar de intercambiar la cantidad mínima de información entre las threads.

Dead Locks

When a multi-thread program becomes unresponsive, it is because the programmer did not correctly structure the exchange variables of the program. A dead lock is produced when there is conflict among the threads of the program. For instance, a thread in waiting on a thread that is blocked and there is no way to unblock it.
Cuando un programa multi-tarea deja de responder, es porque el programador no estructuro en forma correcta las variables de intercambio del programa. Un candado muerto se produce cuando hay un conflicto entre las threads de un programa. Por ejemplo, una thread está esperando a una thread que está bloqueada y no hay forma de desbloquearla.

Tip
The code below shows the implementation of the Mt::IntTs class. As you can see, the class has two member variables: an integer value and a critical section. When a thread wants to set the value of the integer value, the class calls the ::EnterCriticalSection API. The integer value is stored in the integer member variable, and then the ::LeaveCriticalSection is called.
El código de abajo muestra la implementación de la clase Mt::IntTs. Como usted puede ver, la clase tiene dos variables miembro: un valor entero y una sección crítica. Cuando una thread quiere fijar el valor del valor entero, la clase llama la API ::EnterCriticalSection. El valor entero es almacenado en la variable miembro entera, y entonces se llama ::LeaveCriticalSection.

Wintempla.h
...
//____________________________________________________________Int
class IntTs // int Thread Safe
{
public:
     IntTs(void)
     {
          value = 0;
          ::InitializeCriticalSection(&cs);
     }
     ~IntTs(void)
     {
          ::DeleteCriticalSection(&cs);
     }
     void Set(int value)
     {
          ::EnterCriticalSection(&cs);
          this->value = value;
          ::LeaveCriticalSection(&cs);
     }
     int Get(void)
     {
          int b;
          ::EnterCriticalSection(&cs);
          b=value;
          ::LeaveCriticalSection(&cs);
          return b;
     }
     bool SetTry(int value)
     {
          if (::TryEnterCriticalSection(&cs)==0) return false;
          this->value = value;
          ::LeaveCriticalSection(&cs);
          return true;
     }
     bool GetTry(int& value)
     {
          if (::TryEnterCriticalSection(&cs)==0) return false;
          value=this->value;
          ::LeaveCriticalSection(&cs);
          return true;
     }
private:
     int value;
     CRITICAL_SECTION cs;
};


Try (::TryEnterCriticalSection)

As CPU time is wasted every time a thread is waiting to enter a critical section. The program must call ::TryEnterCriticalSection instead. The main difference between ::EnterCriticalSection and ::TryEnterCriticalSection is that ::EnterCriticalSection waits until the critical section become available, while ::TryEnterCriticalSection waits briefly and continues working if the critical section does not become available. ::TryEnterCriticalSection can greatly improve the performance of a multi-thread program.
Como tiempo del CPU se desperdicia cada vez que una thread espera para entrar una sección crítica. El programa puede llamar en su lugar a ::TryEnterCriticalSection. La diferencia principal entre ::EnterCriticalSection y ::TryEnterCriticalSection es que ::EnterCriticalSection espera hasta que la sección crítica se desocupa, mientras que ::TryEnterCriticalSection espera brevemente y continúa trabajado si la sección crítica no se desocupa. ::TryEnterCriticalSection puede mejorar en forma considerable el desempeño de un programa multi-tarea.

PostMessage

PostMessage can used to send notification from a working thread to the main thread (the thread of the main Window). ::PostMessage is extremely useful because it can be used to notify the main thread when a worker thread is about to complete some processing. Microsoft Windows provides a set of messages that can be used for the application: WM_APP+1, WM_ APP +2, WM_ APP +3,... etc. You usually will call ::PostMessage at the end of the thread function to notify the main thread that the thread is about to end. If you post a WM_ APP +1, then the event Window_App1 will be eventually called. You can safely call ::WaitForSingleObject from Window_User1 preventing the main thread to be suspended or be suspended for a long time.
PostMessage puede ser usado para enviar notificaciones desde una thread de trabajo a la thread principal (la thread de la Ventana). ::PostMessage es extremadamente útil porque este puede usarse para notificar a la thread principal cuando una thread de trabajo está a punto de terminar algún procesamiento. Microsoft Windows proporciona una conjunto de mensajes que pueden ser usados por el programa: WM_ APP +1, WM_ APP +2, WM_ APP +3,... etc. Usted usualmente llamará ::PostMessage al final de la función de la thread adicional para notificar a la thread principal que la thread está a punto de terminar. Si usted postead un WM_ APP +1, entonces el evento Window_App1 se ejecutará eventualmente. Usted puede llamar en forma segura ::WaitForSingleObject desde Window_User1 previniendo a la thread principal ser suspendida o ser suspendida por un tiempo muy largo.

Problem 1
Create a Wintempla Dialog application called PiComputation to estimate the value of pi using Wallis formula. Edit the the GUI as shown. To display the symbol of PI use a label with a "p" with the Symbol font.
Cree una aplicación de Dialogo usando Wintempla llamada PiComputation para estimar el valor de pi usando la fórmula de Wallis. Edite la interface gráfica como se muestra. Para mostrar el símbolo de PI se use una etiqueta con una "p" con la fuente de Symbol.

PiFormula

PiComputationGui

Symbol

Step A
Open Wintempla and double click in the interface to open the Dialog properties. Open the Events tab, and check the Timer and User events. When done, close Wintempla. The timer will run periodically to report the status of the worker thread. You must check the status of the working thread sporadically. In this example, the working thread will post a App message (WM_APP) to notify the main thread that work has been completed.
Abra Wintempla y haga doble clic en la interface para abrir las propiedades del dialogo. Abra la pestaña de eventos, y marque los eventos de Timer y User. Cuando termine, cierre Wintempla. El timer se ejecutará en forma periódica para reportar el estado de la worker thread. Usted debe checar el estado de la working thread en forma esporádica. En este ejemplo, la working thread posteará un mensaje de App (WM_APP) para notificar a la thread principal que el trabajo ha sido terminado.

Event1

Event2

Step B
Modify the PiComputation class so that it implements the Mt::IThread interface. Be sure to write correctly the function of the interface. You can include any code in ThreadFunc, in this case we will include the code of the formula of Wallis to estimate the value of pi.
Modifique la clase PiComputation para que implemente la interface Mt::IThread. Asegúrese de escribir en forma correcta la función de la interface. Usted puede incluir cualquier código en ThreadFunc, en este caso nosotros incluiremos el código de la fórmula de Wallis para estimar el valor de pi.

PiComputation.h
#pragma once //______________________________________ PiComputation.h
#include "Resource.h"
#define WORK_ID 100
class PiComputation: public Win::Dialog, public Mt::IThread
{
public:
     PiComputation()
     {
          valuePi = 0.0;
     }
     ~PiComputation()
     {
     }
     Mt::DoubleTs valuePi;
     Mt::ThreadObject threadObject;
     //____________________________________________________________ Mt::IThread
     DWORD ThreadFunc(Mt::BoolTs& cancel, Mt::DecimalTs& progress, Mt::BoolTs& resetTime);
protected:
     ...
};

PiComputacion.cpp
...
void PiComputation::Window_Open(Win::Event& e)
{
     this->btStop.Enabled = false;
}

void PiComputation::btRun_Click(Win::Event& e)
{
     this->timer.Set(1, 1000);
     this->btRun.Enabled = false;
     this->btStop.Enabled = true;
     this->EnableCloseButton(false);
     threadObject.StartThread(*this);
}

void PiComputation::btStop_Click(Win::Event& e)
{
     threadObject.cancel = true;
}

void PiComputation::Window_Timer(Win::Event& e)
{
     if (threadObject.IsStillActive())
     {
          //__________________________ 1. Display progress
          wchar_t text[256];
          threadObject.GetProgressInfo(text, 256);
          this->Text = text;
          //__________________________ 2. Display value of pi so far
          double value = valuePi;
          _snwprintf_s(text, 256, L"%.15f", value);
          tbxResult.Text = text;
     }
}

// cancel: you should return as quickly as possible from ThreadFunc when this variable is true
// progress: use this variable to report the progress of the thread (0.0 to 100.0)
// resetTime: use this variable when the thread has several steps and you want to report progress for each step.
// (if your thread does not have any steps, you can ignore this variable). Set "resetTime" to true, at the beginning of each step
DWORD PiComputation::ThreadFunc(Mt::BoolTs& cancel, Mt::DecimalTs& progress, Mt::BoolTs& resetTime)
{
     resetTime = true;
     double sum=0;
     const int maxIterations = 1000000000;
     for (int i =0; i<maxIterations; i++)
     {
          if (cancel == true) break;
          progress = (100.0*i)/maxIterations;
          //___________________________________________ Perform calculation
          if (i%2 == 0)
               sum += (1.0/(2*i+1));
          else
               sum -= (1.0/(2*i+1));
          valuePi = 4*sum; // update the estimate of pi
     }
     valuePi = 4*sum;
     ::PostMessage(hWnd, WM_APP, (WPARAM)0, (LPARAM)WORK_ID); // Notify the main thread that we are done!
     return 0;
}

void PiComputation::Window_App(Win::Event& e)
{
     if (e.lParam == WORK_ID)
     {
          //____________________________________________ 1. Worker thread has completed
          this->timer.Kill(1); // Stop the timer
          threadObject.WaitForExit(); // Wait until the thread completes
          this->btRun.Enabled = true;
          this->btStop.Enabled = false;
          this->timer.Kill(1); // Stop the timer
          this->Text = L"Done";
          this->EnableCloseButton(true);
          //____________________________________________ 2. Display value of pi
          wchar_t text[256];
          double value = valuePi;
          _snwprintf_s(text, 256, L"%.15f", value);
          tbxResult.Text = text;
     }
}

PiComputationRun

Tip
Comments on Window_Open: The function disables the Stop button.
Comentarios sobre Window_Open: la función deshabilita el botón de Stop.

Tip
Comments on btRun_Click: The function begins by starting a timer that will call the Window_Timer function every second. Then, the Run button is disabled while the Stop button is enabled. The close button of the dialog is disabled so that the user cannot close the program while the program is performing the computation. Finally, a new thread is created when the StartThread functions is called (StartThread internally called ::beginthreadex).
Comentarios sobre btRun_Click: la función comienza por iniciar un timer que llamará la función Window_Timer cada segundo. Entonces, se deshabilita el botón de Run mientras que el botón de Stop se habilita. El botón para cerrar el diálogo se deshabilita para que el usuario no pueda cerrar el programa mientras el programa realiza los cálculos. Finalmente, la nueva thread es creada cuando se llama la función StartThread (StartThread internamente ejecuta ::beginthreadex).

Tip
Comments on btStop_Click: When the user press the Stop button, the exchange variable called cancel is set to true to notify the worker thread that the user wants the worker thread to stop.
Comentarios sobre btStop_Click: Cuando el usuario presiona el botón de Stop, la variable de intercambio llamada cancel es fijada en falso para notificar a la worker thread que el usuario quiere detenerla.

Tip
Comments on Window_Timer: This function is automatically called by the operating system every second. Thus, every second the program display progress information on the title of the dialog and updates the value of pi in the respective textbox.
Comentarios sobre Window_Timer: Esta función es llamada por el sistema operativo en forma automática cada segundo. Así cada segundo, el programa muestra la información de progreso en el título de diálogo y actualiza el valor de pi en la caja de texto respectiva.

Tip
Comments on Window_App: This function is called when the worker thread completes. The program waits for the worker thread to exit, restores the buttons, and kills the timer.
Comentarios sobre Window_App: Esta función es llamada cuando la worker thread completa. El programa espera a que la worker thread termine, restablece los botones y mata el timer.

Tip
Comments on ThreadFunc: The function has a main for-loop to perform the calculations. Inside this for-loop the function checks the value of the exchange variable called cancel. If the value is true, the for-look is broken. Inside this for-loop, the value progress of the computation and the estimate of pi are continually updated.
Comentarios sobre ThreadFunc: La función tiene un ciclo-for principal para realizar los cálculos. Dentro de este ciclo-for la función checa el valor de la variable de intercambio llamada cancel. Si el valor es verdadero, el ciclo-for se interrumpe. Dentro de este ciclo-for, el valor del progreso del cálculo y el estimado de pi son continuamente actualizados.

Wintempla Multi-thread

In Wintempla, you can add an additional thread using Microsoft Visual Studio menu: Tools > Add Wintempla Item... .
En Wintempla, usted puede agregar una thread adicional usando el menú de Microsoft Visual Studio: Tools > Add Wintempla Item... .

MUTEX (MUTual EXclusion)

A mutex is similar to a critical section; it allows only one thread at a time access some resource. The main difference between a mutex and a critical section is that the mutex can be shared among several processes (programs) using a name to identify it. However, critical sections are faster than mutex. You may inspect the Mt::Mutex class to learn more about a mutex. The main functions are:
  1. Mt::Mutex::Create, it internally calls ::CreateMutex
  2. Mt::Mutex::Open, it internally calls ::OpenMutex
  3. Mt::Mutex::Delete, it internally calls ::CloseHandle
  4. Mt::Mutex::Release, it internally calls ::ReleaseMutex
  5. Mt::Mutex::Wait, it internally calls ::WaitForSingleObject

Un mutex es similar a una sección crítica, este permite a una sola thread a la vez tener acceso a un recurso. La diferencia principal entre un mutex y una sección crítica es que el mutex puede ser compartido entre varios procesos (programas) usando un nombre para identificarlo. Sin embargo, las secciones críticas son más rápidas que los mutex. Usted puede inspeccionar la clase Mt::Mutex para aprender más sobre los mutex. Las principales funciones son:
  1. Mt::Mutex::Create, llama internamente a ::CreateMutex
  2. Mt::Mutex::Open, llama internamente a ::OpenMutex
  3. Mt::Mutex::Delete, llama internamente a ::CloseHandle
  4. Mt::Mutex::Release, llama internamente a ::ReleaseMutex
  5. Mt::Mutex::Wait, llama internamente a ::WaitForSingleObject

Semaphore

It allows sharing simultaneously a resource by a maximum number of threads (or processes).You may inspect the Mt::Semaphore class to learn more about semaphores. The main functions are:
  1. Mt::Semaphore::Create, it internally calls ::CreateSemaphore
  2. Mt::Semaphore::Open, it internally calls ::OpenSemaphore
  3. Mt::Semaphore::Delete, it internally calls ::CloseHandle
  4. Mt::Semaphore::Release, it internally calls ::ReleaseSemaphore
  5. Mt::Semaphore::Wait, it internally calls ::WaitForSingleObject

Este permite compartir en forma simultánea un recurso por un número máximo de threads (o procesos). Usted puede inspeccionar la clase Mt::Semaphore para aprender más sobre los semáforos. Las principales funciones son:
  1. Mt::Semaphore::Create, llama internamente a ::CreateSemaphore
  2. Mt::Semaphore::Open, llama internamente a ::OpenSemaphore
  3. Mt::Semaphore::Delete, llama internamente a ::CloseHandle
  4. Mt::Semaphore::Release, llama internamente a ::ReleaseSemaphore
  5. Mt::Semaphore::Wait, llama internamente a ::WaitForSingleObject

Tip
The following tables show a comparison of several synchronization objects.
Las siguientes tablas muestran una comparación entre varios objetos de sincronización.

Name     Critical Section    Mutex    Semaphore  
Kernel object NO YES YES
Good performance YES NO NO
Can be shared among threads YES YES YES
Can be shared among processes NO YES YES

Description      Critical section      Mutex      Semaphore  
Create ::InitializeCriticalSection ::CreateMutex ::CreateSemaphore
Delete ::DeleteCriticalSection ::CloseHandle ::CloseHandle
Open existing --- ::OpenMutex ::OpenSemaphore
Acquire lock ::EnterCriticalSection ::WaitForSingleObject ::WaitForSingleObject
Release lock ::LeaveCriticalSection ::ReleaseMutex ::ReleaseSemaphore

USER mode vs KERNEL mode

A program can be running in USER mode or KERNEL mode. A program runs faster in USER mode than in KERNEL mode. Additionally, it is expensive to switch from one mode to the other. Therefore, you should try to use the Interlocked functions and critical sections instead of mutexes and semaphores.
Un programa puede ser ejecutado en el modo de USER o en el modo de KERNEL. Un programa se ejecuta más rápido en el modo de USER que en el modo de KERNEL. Adicionalmente, es caro cambiar de un modo a otro. Por lo tanto, usted debe tratar de usar las funciones Interlocked y las secciones críticas en lugar de los mutexes y los semáforos.

C++ 11 mutex

Starting in version 11, the language C++ supports for mutex and shared mutex. It defines a set of classes to create a mutex and obtain exclusive access to a resource. A mutex is similar to a critical section. The program below shows how to use a mutex to get exclusive access to the variable a.
A partir de la versión 11, el lenguaje C++ suporta: mutex and shared mutex. Este define un conjunto de clases para crear un mutex y obtener acceso exclusivo a un recurso. Un mutex es similar a una sección crítica. El programa de abajo muestra cómo usar un mutex para conseguir acceso exclusivo a la variable a

Program.cpp
#include <mutex>
//#include <shared_mutex>

void Program::SomeFunction()
{
     std::mutex mx;
     double a = 100.7;
     //_____________________________________ method 1
     {
          std::lock_guard<std::mutex> lock(mx);
          a += 10;
     }
     //_____________________________________ method 2
     mx.lock();
     a += 10;
     mx.unlock();
}


Problem 2
Create a Wintempla Dialog application called PiC11 to estimate the value of pi using Wallis formula using only C++ multithreading support instead of Microsoft Windows multithreading. C++ will use Microsoft Windows multithreading functions in the background. The main advantage of using C++ functions is that is the same code will run on any compiler that support C++ 11 or later. Edit the GUI as shown or just copy the GUI from problem 1. To display the symbol of PI use a label with a "p" with the Symbol font.
Cree una aplicación de Dialogo usando Wintempla llamada PiComputation para estimar el valor de pi usando la fórmula de Wallis usando solamente C++ multithreading sin usar Microsoft Windows multithreading. C++ usará las funciones de Microsoft Windows multithreading por debajo. La diferencia principal de usar las funciones de C++ es que el mismo código correrá en cualquier compilador que soporte C++ 11 o posterior. Edite la interface gráfica como se muestra o sólo copie la GUI del problema 1. Para mostrar el símbolo de PI se use una etiqueta con una "p" con la fuente de Symbol.

PiC11Run

PiC11.h
#pragma once //______________________________________ PiC11.h
#include "Resource.h"
#include <thread>
#include <atomic>
#include <mutex>
#define WORK_ID 100

class PiC11: public Win::Dialog
{
public:
     PiC11()
     {
          cancel = false;
          progress = 0;
          valuePi = 0.0;
     }
     ~PiC11()
     {
     }
     std::atomic<bool > cancel;
     std::atomic<unsigned int> progress;
     std::mutex mutexPi;
     double valuePi;
     std::thread th;
     static void ThreadFunc(HWND hWnd, std::atomic<bool >& cancel, std::atomic<unsigned int>& progress, double& valuePi, std::mutex& mutexPi);
     ...
};


PiC11.cpp
...

void PiC11::Window_Open(Win::Event& e)
{
     this->btStop.Enabled = false;
     if (cancel.is_lock_free() == false) this->MessageBox(L"std::atomic<bool > is not supported", L"PiC11", MB_OK | MB_ICONERROR);
     if (progress.is_lock_free() == false) this->MessageBox(L"std::atomic<unsigned int> is not supported", L"PiC11", MB_OK | MB_ICONERROR);
}

void PiC11::btRun_Click(Win::Event& e)
{
     this->cancel = false;
     this->progress = 0;
     this->valuePi = 0.0;
     this->timer.Set(1, 1000);
     this->btRun.Enabled = false;
     this->btStop.Enabled = true;
     this->EnableCloseButton(false);
     th = std::thread(ThreadFunc, hWnd, std::ref(cancel), std::ref(progress), std::ref(valuePi), std::ref(mutexPi));
}

void PiC11::btStop_Click(Win::Event& e)
{
     cancel = true;
}

void PiC11::Window_App(Win::Event& e)
{
     if (e.lParam == WORK_ID)
     {
          //____________________________________________ 1. Worker thread has completed
          this->timer.Kill(1); // Stop the timer
          th.join();
          this->btRun.Enabled = true;
          this->btStop.Enabled = false;
          this->timer.Kill(1); // Stop the timer
          this->Text = L"Done";
          this->EnableCloseButton(true);
          //____________________________________________ 2. Display value of pi
          wchar_t text[256];
          mutexPi.lock();
          _snwprintf_s(text, 256, L"%.15f", valuePi);
          mutexPi.unlock();
          tbxResult.Text = text;
     }
}

void PiC11::Window_Timer(Win::Event& e)
{
     //__________________________ 1. Display progress
     wchar_t text[256];
     _snwprintf_s(text, 256, _TRUNCATE, L"%.2f%%", progress/100.0);
     this->Text = text;
     //__________________________ 2. Display value of pi so far
     double value = valuePi;
     _snwprintf_s(text, 256, L"%.15f", value);
     tbxResult.Text = text;
}

void PiC11::ThreadFunc(HWND hWnd, std::atomic<bool >& cancel, std::atomic<unsigned int>& progress,     double& valuePi, std::mutex& mutexPi)
{
     double sum=0;
     const int maxIterations = 1000000000;
     for (int i =0; i<maxIterations; i++)
     {
          if (cancel == true) break;
          progress = (int)((10000.0*i)/maxIterations);
          //___________________________________________ Perform calculation
          if (i%2 == 0)
               sum += (1.0/(2*i+1));
          else
               sum -= (1.0/(2*i+1));
          mutexPi.lock();
          valuePi = 4*sum; // update the estimate of pi
          mutexPi.unlock();
     }
     ::PostMessage(hWnd, WM_APP, (WPARAM)0, (LPARAM)WORK_ID); // Notify the main thread that we are done!
}


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