AlwaysLearningNewStuff
AlwaysLearningNewStuff

Reputation: 3031

Non blocking thread synchronization

I have a dialog box with a button, and some other controls.

When that button is pressed a worker thread is spawned.

For easier discussion, let us just say that thread function does a lengthy job.

Each time button is clicked, new thread should spawn and do its stuff.

Dialog box should not be blocked while worker threads do their job, since user should be able to minimize it, click on other controls and so on.

On Wikipedia, i have found a term lock-free algorithm, which refers to non blocking thread synchronization.

This is why I am interested in non blocking thread synchronization. I believe that this will ensure the behavior I need.

I am new to multithreading, but I did find some articles/answered questions about it here, and have read the documentation on Microsoft about it.

Most of these use the following algorithms:

1.

Main thread declares a volatile variable ( usually int or bool ) and passes it to the worker thread. Worker thread checks the variable in a loop, and if it is not set to indicate its termination, continues to do its work. When it needs to be terminated, parent thread sets the variable.

2.

There is also a vast documentation on Microsoft's website about synchronization. There, I have found mutexes, critical sections, interlocks and much more.

The problem with these is that they always block the parent thread ( usually with WaitForSingleObject or WaitForMultipleObjects API ), until worker thread is finished.

Also, searching here, I have found a recent question ( Abort thread properly when dialog box close button is clicked ) that has helped me make a start.

In order to solve my problem I will break this question into multiple ones, and post them separately, in order to respect the rules of the StackOverflow.

So my first question for now is:

Which API/algorithm should I use to achieve non blocking thread synchronization, which will help me to achieve dialog box behaves as described above ?

I would also appreciate links to tutorials or code examples, if possible, since I had no luck with Google ( also, developers learn best through code/pseudo code ).

I will present my initial tries with these code snippets bellow, just in case they prove to be useful:

   // thread function

   DWORD WINAPI MyThread()
   {
       int result = 0;

       // do something

       if( /** everything is OK **/ )
           return result;
       else
       {
           result = 1; 

           return result;
       }
   }

Now the dialog box:

  // button clicked handler

  case IDC_BUTTON1:
        {
            // create thread

            DWORD tID;

            HANDLE th = CreateThread( NULL , 0 , 
                        (LPTHREAD_START_ROUTINE)MyThread , 
                        NULL , 0 , &tID );

            if( !th )
                EndDialog( hWnd, IDCANCEL );

            CloseHandle( th );
         }
         break;

   case IDCANCEL:

         EndDialog( hWnd, IDCANCEL );

         break;

At this point, when I run the program, activate dialog box, and click on the button, worker thread spawn up, and do well if I wait it to finish.

Yet if I close the dialog box early, the nature of my problem is the same as in the question I have found here ( I think that WaitForMultipleObjects in IDCANCEL handler can partialy fix this but I will leave it for another post ).

EDIT #1:

I have posted new code, to reflect my progress and problems I have faced.

IMPORTANT NOTE:

Instead of firing up multiple threads, I have hidden the button that starts a thread, once it is pressed.

This means that user will have to wait for first thread to finish, before he/she is able to activate second one.

This was done for easier debugging.

Defined custom messages to indicate if thread exited gracefully, or an error happened.

    #define WM_THREAD_OK      ( WM_APP + 1 )

    #define WM_THREAD_ERROR   ( WM_APP + 2 )

Added data structure that will be passed to thread so it can communicate with a dialog, and so it can be aborted properly

    struct Data
    {
        HWND hwnd;

        bool bContinue;
    };

Reworked thread function to send messages to a dialog box so it can notify dialog about errors, or graceful end.

New code ( this was based on the example from the book Charles Petzold-Programming Windows 5th ed. ):

   // thread function

   DWORD WINAPI MyThread( LPVOID lpvoid )
   {

       HRESULT hr;

       volatile Data *data = ( Data* )lpvoid;

       try
       {
          for( int i = 0; i < 10 && data->bContinue; i++ )
          {
              hr = // do something

              if( FAILED(hr) )
                  throw _com_error(hr);
          }

          // once you leave loop, check if thread was aborted, or all was well

          if( ! ( data->bContinue ) )
          {
              // thread was aborted, do cleanup and exit

              // cleanup

              return 0;
          }

          // if all went well, do cleanup, and "say" so to the dialog box

          // do cleanup here

          SendMessage( data->hwnd, WM_THREAD_OK, 0, 0 );

          return 0; // exit gracefully
       }
       catch( _com_error & )
       {
           // do cleanup

           SendMessage( data->hwnd, WM_THREAD_ERROR, 0, 0 );

           return 1;
       }
   }

Here is new code for dialog box ( NOTE: dialog box is MODELESS now ):

  static Data data;

  HANDLE th = NULL;

  // button clicked handler

  case IDC_BUTTON1:
        {
            // create thread

            DWORD tID;

            th = CreateThread( NULL , 0 , 
                        (LPTHREAD_START_ROUTINE)MyThread , 
                        (LPVOID)&data, 0 , &tID );

            if( !th )
                DestroyWindow( hwnd );

            // hide the button which activates thread

            ShowWindow( GetDlgItem( hwnd, IDC_BUTTON1 ), SW_HIDE );
         }
         break;

   case WM_THREAD_OK:

     if( th )
         CloseHandle( th );

         ShowWindow( GetDlgItem( hwnd, IDC_BUTTON1 ), SW_SHOW );

     break;

   case WM_THREAD_ERROR:

       if( th )
           CloseHandle( threadHandle );

       MessageBox( hwnd, L"Error", L"Error", MB_ICONERROR );

       break;

   case IDCANCEL:

         data.bContinue = false; // set thread abortion flag

         if( th )
         {
            // after I comment out below statement, thread aborts properly

            // WaitForSingleObject( th, INFINITE );

            CloseHandle( th );
         }

         DestroyWindow( hwnd ); // dialog box is now modeless one!

         break;

My question is: Are there any mistakes in this code ? Can it be improved, and how ?

Thank you.

Upvotes: 2

Views: 3971

Answers (2)

Gabriel
Gabriel

Reputation: 3604

You can proced this way:

  1. Create one loop that contains all the workers thread. That's a whle True loop
  2. Launch this thread in your main (or in the constructor of your dialog box)
  3. The loop will take care of launching some threads and wait for it

Here is an example I have made: it is made out of a main and with C++ 11 but it can be transposed (ask me for more if you need more info or you disagree).

#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <cstdlib>
#include <iostream>
#include <ctime>
using namespace std;

std::mutex m;
std::condition_variable cv;

atomic<bool> WorkerAvailable=true;
atomic<int> RandomVariable;

void WorkersLoop();
void worker_thread();

//this gathers the threads
void WorkersLoop()
{
    std::srand(std::time(0)); 
    int random_variable;
    //infinite loop for handling all the worker threads
    while (true)
    {
        random_variable= std::rand();
        //this actually simulates the user pressing a button
        if (random_variable%5==0)
        {
            // wait for the worker
            {
                std::unique_lock<std::mutex> lk(m);
                cv.wait(lk, []{return WorkerAvailable==true;});
            }
            std::lock_guard<mutex> lk(cout_mutex);
            {
                cout<<"thread started, random variable= "<<random_variable<<"\n";
            }
                    //this launches the actual work to be done
            std::thread abc(&worker_thread);
            // wait for the worker
            {
                std::unique_lock<std::mutex> lk(m);
                cv.wait(lk, []{return WorkerAvailable==true;});
            }
            abc.join();
        }
    }
}

//this does the work you want
void worker_thread()
{
    WorkerAvailable=false;

    std::this_thread::sleep_for(std::chrono::seconds(1));

    cout<<"thread  "<< this_thread::get_id()<<"finished"<<std::endl;

    WorkerAvailable=true;
    cv.notify_one();
}


int main()
{
        //launching the loop responsible for doing the threads
    thread Loop(WorkersLoop);
    Loop.join();
    return 0;
}

Hope that helps, if it does not answer your question please tell me I will change or delete this post

Upvotes: 0

pm100
pm100

Reputation: 50210

you dont need lock free synchronization. Just fire up the threads and have them signal objects when complete. Do a non-blocking call on the sync handle if you dont want to block the UI thread

ie

WaitForMultipleObjects(n,lpHandles, 0, 0);

the last 0 says return rather than wait if object(s) not signalled

Upvotes: 0

Related Questions