非阻塞线程同步

时间:2013-08-12 22:26:10

标签: c++ multithreading winapi

我有一个带有按钮的对话框,还有一些其他控件。

按下该按钮时会生成一个工作线程。

为了便于讨论,我们只想说线程函数做了很长的工作。

每次单击按钮时,新线程都会生成并执行其中的操作。

工作线程完成工作时不应阻止

对话框,因为用户应该能够最小化它,点击其他控件等等。

在维基百科上,我找到了一个术语无锁算法,它指的是非阻塞线程同步。

这就是我对非阻塞线程同步感兴趣的原因。我相信这将确保我需要的行为。

我是多线程的新手,但我确实在这里找到了一些关于它的文章/回答问题,并且已经阅读了关于它的Microsoft文档。

其中大多数使用以下算法:

1

主线程声明一个volatile变量(通常是int或bool)并将其传递给工作线程。工作线程在循环中检查变量,如果未设置为指示其终止,则继续执行其工作。当需要终止时,父线程设置变量。

2

微软网站上还有关于同步的大量文档。在那里,我发现了互斥体,关键部分,互锁等等。

这些问题是它们总是阻塞父线程(通常使用WaitForSingleObject或WaitForMultipleObjects API),直到工作线程完成。

另外,在这里搜索,我发现了一个帮助我开始的最近一个问题(Abort thread properly when dialog box close button is clicked)。

为了解决我的问题,我将这个问题分解为多个问题,然后分别发布,以便遵守StackOverflow的规则。

所以我现在的第一个问题是:

我应该使用哪种API /算法来实现非阻塞线程同步,这有助于我实现如上所述的对话框行为?

如果可能的话,我也非常感谢教程或代码示例的链接,因为我对Google没有好运(同样,开发人员通过代码/伪代码学习得最好)。

我将在下面的代码片段中展示我的初步尝试,以防它们被证明是有用的:

   // thread function

   DWORD WINAPI MyThread()
   {
       int result = 0;

       // do something

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

           return result;
       }
   }

现在对话框:

  // 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;

此时,当我运行程序时,激活对话框,然后单击按钮,工作线程产生,如果我等待完成,则表现良好。

然而,如果我提前关闭对话框,我的问题的性质与我在这里找到的问题相同(我认为IDCANCEL处理程序中的WaitForMultipleObjects可以部分解决这个问题,但我将把它留给另一个帖子)。

编辑#1:

我发布了新代码,以反映我所面临的进展和问题。

重要提示:

我没有启动多个线程,而是在按下后隐藏了启动线程的按钮。

这意味着用户必须等待第一个线程完成,然后他/她才能激活第二个线程。

这样做是为了便于调试。

定义自定义消息以指示线程是否正常退出,或发生错误。

    #define WM_THREAD_OK      ( WM_APP + 1 )

    #define WM_THREAD_ERROR   ( WM_APP + 2 )

添加了将传递给线程的数据结构,以便它可以与对话框进行通信,因此可以正常中止

    struct Data
    {
        HWND hwnd;

        bool bContinue;
    };

重写的线程函数将消息发送到对话框,以便它可以通知对话框有关错误或优雅结束。

新代码(这是基于Charles Petzold编程的Windows第5版中的示例):

   // 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;
       }
   }

这是对话框的新代码(注意:对话框现在是MODELESS):

  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;

我的问题是:此代码中是否有任何错误?可以改进,怎么样?

谢谢。

2 个答案:

答案 0 :(得分:1)

你不需要无锁同步。只需启动线程并在完成后让它们发出信号对象。如果您不想阻止UI线程,请在同步句柄上执行非阻塞调用

WaitForMultipleObjects(n,lpHandles, 0, 0);

最后0表示返回而不是等待对象未发出信号

答案 1 :(得分:0)

您可以这样处理:

  1. 创建一个包含所有工作线程的循环。那是一个真正的循环
  2. 在您的主要(或对话框的构造函数)中启动此主题
  3. 循环将负责启动一些线程并等待它
  4. 这是我做的一个例子:它是由一个主要的和C ++ 11组成的,但它可以被转置(如果你需要更多信息或者你不同意,请向我询问更多信息)。

    #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;
    }
    

    希望有帮助,如果它没有回答你的问题,请告诉我,我将更改或删除这篇文章