我有一个带有按钮的对话框和一些其他控件。
当按下该按钮时,将产生一个工作线程。
为了便于讨论,我们只说线程函数做了一个冗长的工作。
每次单击按钮时,都应生成新线程并执行其工作。
工作线程执行其工作时不应阻止对话框,因为用户应该能够将其最小化,单击其他控件等等。
在维基百科上,我找到了一个术语无锁算法,它指的是非阻塞线程同步。
这就是我对非阻塞线程同步感兴趣的原因。我相信这将确保我需要的行为。
我是多线程的新手,但我确实在这里找到了一些关于它的文章/已回答的问题,并且已经阅读了关于它的 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;
我的问题是:这段代码有错误吗?可以改进吗,如何改进?
谢谢你。