2012-01-25 48 views
4

请考虑下面的代码片段。使用GCC 4.6.1,x变为0y变成1C++ 11线程拼图中的仿函数

为什么在使用和不使用单独的线程时会得到不同的结果?我应该如何修改代码以使两个版本的结果相同(即整数值增加1)

谢谢。

struct functor{ 
    void operator()(int & x){ 
     ++x; 
    }  
}; 

void tfunc(functor & f, int & x){ 
    f(x); 
} 

int main(){ 
    functor f; 
    int x = 0, y = 0; 
    std::thread t = std::thread(tfunc, f, x); 
    t.join(); 
    std::cout << "with thread " << x << std::endl;  
    f(y); 
    std::cout << "without thread " << y << std::endl; 
} 
+1

尝试使'x'易失性。编译器在'std :: thread'创建之后但join之前返回它的值是合法的。 – spraff

+2

@spraff:'volatile'不能代替正确的同步(并且这不是同步问题)。 –

+0

更新。如果'int *'传递给'tfunc'而不是'int&',它就可以工作。特雷斯怪异。 – user92382

回答

2

很容易看出发生了什么。只需将int替换为不可复制类型(其中一个具有私有副本构造函数),编译器将精确定位libstdc++试图复制参数而不是使用引用的确切位置。在我的情况下,它是<tuple>标准标题中的第138行。

不管这个标准是否正确执行,目前我都看不出来。

UPDATE的标准说的std::thread::thread每个参数应满足MoveConstructible要求,并传递给线程函数的实际参数是移动构建的从std::thread::thread参数。这意味着,

  1. 线程函数得到的参数的拷贝,并
  2. 原件可能在过程中被破坏。

所以通过引用传递的东西是行不通的。

+1

在这种情况下,我估计修复是针对'std :: thread'的实现来添加重载来包装'std :: ref'中的引用参数,对吧? – spraff

+0

@spraff:查看更新。 –

+0

@spraff:无需修复 - std :: thread已经处理std :: ref。如果你把程序改成'std :: thread t = std :: thread(tfunc,f,std :: ref(x));',它就会起作用。这正是'std :: ref'存在的情况。 –

0

它看起来像是当std::thread(tfunc, f, x)被调用时,x被复制并且临时值的引用被传递给函子,因此对函子的调用不会改变x的值。我认为一般STL算法/函数总是复制参数。如果你想让你的函数调用改变x,你可以考虑使用指针,因为即使指针被复制,副本仍然指向相同的地址。