这个多线程C++代码需要'volatile'吗?

gab*_*lin 11 c++ multithreading volatile

我用C++编写了一个Windows程序,它有时使用两个线程:一个后台线程用于执行耗时的工作; 和另一个用于管理图形界面的线程.这样,程序仍然响应用户,这需要能够中止某个操作.线程通过共享bool变量进行通信,该变量设置为true当GUI线程通知工作线程中止时.这是实现此行为的代码(我已经删除了不相关的部分):

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

    ...

};
Run Code Online (Sandbox Code Playgroud)

工人线程执行的代码


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

    ...

};
Run Code Online (Sandbox Code Playgroud)

threadParameterAbort_在两个线程中都指向bool在结构中声明的变量,该变量在创建时传递给工作线程.它被宣布为

bool volatile abortExecution_;
Run Code Online (Sandbox Code Playgroud)

我的问题是:我需要在volatile这里使用,上面的代码是否足以确保程序是线程安全的?我推断在volatile这里使用这个问题的理由(参见这个问题的背景)是它将:

  • 阻止读取*threadParameterAbort_使用缓存,而是从内存中获取值,并且

  • 防止编译器if因优化而删除工作线程中的子句.

(下面的段落只关心程序的多线程安全等,并没有,我再说一遍,涉及声称volatile以任何方式提供保证线程安全的任何手段).据我所知,它应该是线程安全的,因为bool在大多数(如果不是全部)架构中,设置变量应该是原子操作.但我可能是错的.而且我也担心编译器是否可能重新排序指令,例如破坏线程安全性.但更好的是安全(没有双关语意)而不是抱歉.

编辑: 我的措辞中的一个小错误使问题看起来好像我在问是否volatile足以确保线程安全.这不是我的意图 - volatile确实不能确保任何方式的线程安全 - 但我要问的是,上面提供的代码是否表现出正确的行为以确保程序是线程安全的.

dor*_*ron 14

您不应该依赖volatile来保证线程安全,这是因为即使编译器将保证始终从内存中读取变量(而不是寄存器缓存),在多处理器环境中也需要内存屏障.

而是在共享内存周围使用正确的锁.像关键部分这样的锁通常非常轻量级,并且在没有争用的情况下可能都是用户实现的.它们还将包含必要的记忆障碍.

Volatile仅应用于内存映射IO,其中多个读取可能返回不同的值.类似地,对于内存映射写入

  • 互斥或信号量是非纯函数.它可能有副作用(更改全局变量),因此编译器可能不会假设*threadParamAbort_的值将保持不变.并且必须在互斥锁或临界区锁定或信号之后重新读取.并且因为一次只有一个线程可以容纳一个关键部分,所以在锁内部重新排序指令没有任何问题.只需确保正确应用锁定即可. (2认同)

Ser*_*pth 9

维基百科说得很好.

在C和C++中,volatile关键字旨在允许访问内存映射设备,允许在setjmp之间使用变量,允许在信号处理程序中使用sig_atomic_t变量

对volatile变量的操作不是原子的,也不是为线程建立正确的先发生关系.这是根据相关标准(C,C++,POSIX,WIN32),这对于绝大多数当前实现来说都是事实.volatile属性作为便携式线程构造基本上没有价值.

  • 为什么,替代方案是什么都不引用.或者我可以引用C或C++文档,这些文档本来就难以理解.此外,它不完全像我在这里提交论文.如果有人在维基百科上简洁地回答,我将链接到维基百科.我根本不理解膝盖混蛋维基百科的仇恨,也不理解这些任意的发布规则,其他选项本来就是简单引用没有来源,正如大多数答案所做的那样. (6认同)
  • 我不明白为什么人们一直认为`volatile`提供任何类型的线程安全.+1 (4认同)
  • 维基百科是一个可怕的引用来源(甚至维基百科的创始人说不要使用维基百科作为报价来源).用它作为研究的起点,但至少引用一个权威来源. (4认同)
  • @gablin:不,你误解了我.`volatile`与线程安全无关*.完全没有. (3认同)