我有一个带有按钮的对话框,还有一些其他控件。
按下该按钮时会生成一个工作线程。
为了便于讨论,我们只想说线程函数做了很长的工作。
每次单击按钮时,新线程都会生成并执行其中的操作。
工作线程完成工作时不应阻止对话框,因为用户应该能够最小化它,点击其他控件等等。
在维基百科上,我找到了一个术语无锁算法,它指的是非阻塞线程同步。
这就是我对非阻塞线程同步感兴趣的原因。我相信这将确保我需要的行为。
我是多线程的新手,但我确实在这里找到了一些关于它的文章/回答问题,并且已经阅读了关于它的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可以部分解决这个问题,但我将把它留给另一个帖子)。
重要提示:
我没有启动多个线程,而是在按下后隐藏了启动线程的按钮。
这意味着用户必须等待第一个线程完成,然后他/她才能激活第二个线程。
这样做是为了便于调试。
定义自定义消息以指示线程是否正常退出,或发生错误。
#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;
我的问题是:此代码中是否有任何错误?可以改进,怎么样?
谢谢。
答案 0 :(得分:1)
你不需要无锁同步。只需启动线程并在完成后让它们发出信号对象。如果您不想阻止UI线程,请在同步句柄上执行非阻塞调用
即
WaitForMultipleObjects(n,lpHandles, 0, 0);
最后0表示返回而不是等待对象未发出信号
答案 1 :(得分:0)
您可以这样处理:
这是我做的一个例子:它是由一个主要的和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;
}
希望有帮助,如果它没有回答你的问题,请告诉我,我将更改或删除这篇文章