2

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

当按下该按钮时,将产生一个工作线程。

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

每次单击按钮时,都应生成新线程并执行其工作。

工作线程执行其工作时不应阻止对话框,因为用户应该能够将其最小化,单击其他控件等等。

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

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

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

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

1.

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

2.

微软网站上还有大量关于同步的文档。在那里,我发现了互斥锁、临界区、互锁等等。

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

另外,在这里搜索,我发现了一个最近的问题(单击对话框关闭按钮时正确中止线程),它帮助我开始了。

为了解决我的问题,我将把这个问题分成多个问题,并分别发布,以尊重 StackOverflow 的规则。

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

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

如果可能的话,我也会感谢指向教程或代码示例的链接,因为我在谷歌上没有运气(而且,开发人员通过代码/伪代码学习得最好)。

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

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

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

  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;

我的问题是:这段代码有错误吗?可以改进吗,如何改进?

谢谢你。

4

2 回答 2

1

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

IE

WaitForMultipleObjects(n,lpHandles, 0, 0);

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

于 2013-08-12T22:30:07.677 回答
0

你可以这样处理:

  1. 创建一个包含所有工作线程的循环。这是一个真正的循环
  2. 在您的主线程(或对话框的构造函数中)启动此线程
  3. 循环将负责启动一些线程并等待它

这是我制作的一个示例:它由 main 和 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;
}

希望对您有所帮助,如果它不能回答您的问题,请告诉我我将更改或删除此帖子

于 2013-08-17T14:38:50.077 回答