2014-01-11 88 views
1

我很少想到在两个连续的表达式之间,函数调用和它的主体的第一个表达式的执行之间,或者在构造函数的调用和它的执行之间发生了什么初始化。然后,我开始阅读关于并发...std :: mutex锁定的顺序

1)在连续两次调用std::thread的构造用相同的可调用(如函数,仿函数,拉姆达),其身体开始有std::lock_guard初始化与同std::mutex对象,标准是否保证第一个thread构造函数调用对应的线程首先执行受锁定保护的代码?如果标准没有作出保证,那么是否存在任何理论上或实践上的可能性,对应于第二个构造函数调用的线程首先执行受保护的代码? (第一thread构造函数调用的初始化或身体的执行期间例如重系统负载)

这里的一个全局std::mutexm对象和全局unsignednum初始化为1。功能foo的开启支架{std::lock_guard之间只有空白。在main中,有两个std::threadt1t2t1首先调用线程构造函数。 t2第二次调用线程构造函数。每个线程都使用指向foo的指针构造。 t1调用foounsigned参数1t2调用foounsigned参数2。取决于哪个线程先锁定mutexnum的值将是43,这两个线程都执行了锁定保护代码。 num将等于4如果t1击败t2锁。否则,num将等于3。我在每个循环结束时通过循环并重置num1来运行100,000次试验。 (据我所知,这样做的结果,也不应该依赖于哪个线程是join()编第一。)

#include <thread> 
#include <mutex> 
#include <iostream> 

std::mutex m; 
unsigned short num = 1; 

void foo(unsigned short par) { 
    std::lock_guard<std::mutex> guard(m); 
    if (1 == num) 
     num += par; 
    else 
     num *= par; 
} 

int main() { 
    unsigned count = 0; 
    for (unsigned i = 0; i < 100000; ++i) { 
     std::thread t1(foo, 1); 
     std::thread t2(foo, 2); 
     t1.join(); 
     t2.join(); 
     if (4 == num) { 
      ++count; 
     } 
     num = 1; 
    } 
    std::cout << count << std::endl; 
} 

最后,count等于100000,所以原来t1每赢得比赛时间。但这些试验无法证明任何事情。

3.)标准任务“首先调用thread构造函数”总是暗示“首先调用传递给thread构造函数的可调用函数”?

4.)标准任务“首先调用传递给thread构造函数的可调用函数”是否始终暗示“首先锁定mutex”;前提是在可调用函数体内,不存在代码,该代码依赖于初始化行之前传递给可调用函数的参数吗? (也排除任何可调用的本地static变量,像所谓的次数的计数器,可用于故意拖延某些呼叫。)

+2

不,是的,不清楚,没有。 –

回答

3
  1. 没有,该标准并不能保证第一个线程获取先锁定。基本上,如果你需要在线程间施加和排序,你需要在这些线程之间进行同步。即使第一个线程首先调用互斥锁函数,第二个线程也可能首先获取锁。
  2. 绝对。例如,在线程产生时可能只有一个内核可用于您的应用程序,并且如果产生线程在第二个线程产生以等待某些事件后决定,那么计划可能会决定处理最新的线程,第二个线程。即使有很多内核可用,但第二个线程速度更快的原因仍然很多。
  3. 不,为什么会这样!第一步是产生一个线程并继续。到第一个函数对象被调用时,第二个线程可以运行并调用它的函数对象。
  4. 不可以。没有线程之间的顺序保证,除非您明确强制执行它们,因为它们会破坏并发的目的。