当编写需要在应用程序和中断例程/线程/回调例程之间共享文件范围变量的C程序时,众所周知变量必须声明为volatile,否则编译器可能会出错优化。这是我的意思的一个例子:
int flag;
void some_interrupt (void)
{
flag = 1;
}
int main()
{
flag = 0;
...
/* <-- interrupt occurs here */
x = flag; /* BUG: the compiler doesn't realize that "flag" was changed
and sets x to 0 even though flag==1 */
}
为了防止上述错误,“flag”应该被声明为volatile。
我的问题是:在创建包含线程的类时,这如何适用于C ++?
我的班级看起来像这样:
class My_thread
{
private:
int flag;
static void thread_func (void* some_arg) // thread callback function
{
My_thread* this_ptr= (My_thread*)some_arg;
}
};
“some_arg”将包含指向该类实例的指针,因此“My_thread”的每个对象都有自己的线程。通过这个指针,它将访问成员变量。
这是否意味着必须将“this_ptr”声明为指针易变数据?必须“标志”也是不稳定的吗?如果是这样,我是否必须使所有成员函数修改“flag”volatile?
我对特定操作系统或编译器的行为不感兴趣,我正在寻找一种通用的,完全可移植的解决方案。
编辑:这个问题与线程安全无关!
真正的代码将有信号量等。
为了澄清,我希望避免因编译器不知道可能从程序本身以外的源调用回调函数而导致的错误,因此对是否使用了某些变量做出了错误的结论。我知道如何在C中执行此操作,如第一个示例所示,但不是在C ++中。
答案 0 :(得分:2)
那么,这种编辑会让世界变得与众不同。信号量引入了记忆障碍。那些使volatile
多余。在对信号量进行任何操作后,编译器将始终重新加载int flag
。
volatile
是不够的,并且在存在锁的情况下减少。这使得它对线程安全编程毫无用处。
答案 1 :(得分:0)
从函数指针签名我猜你正在使用线程的posix线程实现。我假设您想知道如何使用此API启动线程。首先考虑使用boost thread代替。如果不是一个选项,我通常会选择以下内容来获得一些舒适的Java可读性。
class Runnable {
public:
virtual void run() = 0;
};
class Thread : public Runnable {
public:
Thread();
Thread(Runnable *r);
void start();
void join();
pthread_t getPthread() const;
private:
static void *start_routine(void *object);
Runnable *runner;
pthread_t thread;
};
然后start_routine
函数中出现类似的内容:
void* Thread::start_routine(void *object) {
Runnable *o = (Runnable *)object;
o->run();
pthread_exit(NULL);
return NULL;
}
现在访问扩展Runnable或Thread类的类的字段不必是volatile,因为它们是 thread-local 。
那就是说,如果这就是你所要求的那样,那么在线程之间共享数据比使用易失性数据成员更复杂......
答案 2 :(得分:0)
答案 3 :(得分:0)
阅读Andrei Alexandrescu在Dobbs博士的这篇文章,它可能是相关的:
volatile - Multithreaded Programmer's Best Friend
从介绍到文章:
设计了volatile关键字 防止编译器优化 可能会导致代码不正确 存在某些异步 事件。例如,如果您声明一个 原始变量为volatile, 不允许编译器对其进行缓存 在寄存器中 - 一种常见的优化 如果这样那将是灾难性的 变量在多个之间共享 线程。所以一般规则是,如果 你有原始类型的变量 必须在多个之间共享 线程,声明那些变量 易挥发。但你实际上可以做到 使用此关键字可以获得更多:您可以 用它来捕获不是的代码 线程安全,你可以这样做 编译时间。这篇文章说明了如何 它完成了;解决方案涉及到 简单的智能指针,也使 很容易序列化关键部分 代码。