我用 C++ 编写了一个 Windows 程序,它有时使用两个线程:一个后台线程用于执行耗时的工作;和另一个用于管理图形界面的线程。这样程序仍然响应用户,这是能够中止某个操作所必需的。线程通过共享的 bool
变量进行通信,当 GUI 线程向工作线程发出信号以中止时,该变量设置为 true
。以下是实现此行为的代码(我已删除不相关的部分):
GUI 线程执行的代码
class ProgressBarDialog : protected Dialog {
/**
* This points to the variable which the worker thread reads to check if it
* should abort or not.
*/
bool volatile* threadParameterAbort_;
...
BOOL CALLBACK ProgressBarDialog::DialogProc( HWND dialog, UINT message,
WPARAM wParam, LPARAM lParam ) {
switch( message ) {
case WM_COMMAND :
switch ( LOWORD( wParam ) ) {
...
case IDCANCEL :
case IDC_BUTTON_CANCEL :
switch ( progressMode_ ) {
if ( confirmAbort() ) {
// This causes the worker thread to be aborted
*threadParameterAbort_ = true;
}
break;
}
return TRUE;
}
}
return FALSE;
}
...
};
工作线程执行的代码
class CsvFileHandler {
/**
* This points to the variable which is set by the GUI thread when this
* thread should abort its execution.
*/
bool volatile* threadParamAbort_;
...
ParseResult parseFile( ItemList* list ) {
ParseResult result;
...
while ( readLine( &line ) ) {
if ( ( threadParamAbort_ != NULL ) && *threadParamAbort_ ) {
break;
}
...
}
return result;
}
...
};
两个线程中的 threadParameterAbort_
都指向在结构中声明的 bool
变量,该结构在创建时传递给工作线程。它被声明为
bool volatile abortExecution_;
我的问题是:我需要在这里使用volatile
吗?上面的代码是否足以确保程序是线程安全的?我在这里证明使用 volatile
的理由(参见 this question 了解背景)是它将:
防止读取
*threadParameterAbort_
以使用缓存,而是从内存中获取值,并且防止编译器因优化而删除工作线程中的
if
子句。
(以下段落仅关注程序本身的线程安全,不,我重复一遍,不涉及声明 volatile
以任何方式提供确保线程安全的任何方法。)据我所知,它应该是线程安全的,因为 bool
变量的设置应该在大多数情况下(如果不是全部的话) ,架构是一个原子操作。但我可能是错的。而且我还担心编译器是否会重新排序指令,例如破坏线程安全。但最好是安全(没有双关语意)而不是抱歉。
编辑:
我措辞中的一个小错误使这个问题看起来好像我在问 volatile
是否足以确保线程安全。这不是我的意图——volatile
确实不能以任何方式确保线程安全——但我想问的是上面提供的代码是否表现出正确的行为以确保程序是线程的-安全。
最佳答案
你不应该依赖 volatile 来保证线程安全,这是因为即使编译器会保证变量总是从内存中读取(而不是寄存器缓存),在多处理器环境中内存屏障也会被要求。
而是在共享内存周围使用正确的锁。像关键部分这样的锁通常非常轻量级,在没有争用的情况下可能会全部在用户端实现。它们还将包含必要的内存屏障。
Volatile 应该只用于多次读取可能返回不同值的内存映射 IO。内存映射写入也是如此。
关于c++ - 此多线程 C++ 代码中是否需要 'volatile'?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3612505/