std :: mutex锁定的顺序

时间:2014-01-11 01:06:32

标签: c++ multithreading locking mutex stdthread

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

1。)连续两次调用具有相同可调用性的std::thread构造函数(例如函数,函数,lambda),其主体以std::lock_guard初始化开始,具有相同的std::mutex对象,标准是否保证与第一个thread构造函数调用相对应的线程首先执行受锁保护的代码?

2.。)如果标准没有做出保证,那么第二个thread构造函数调用对应的线程是否有理论或实际可能首先执行受保护的代码? (例如,在执行初始化程序或第一个thread构造函数调用的主体期间加载了大量系统)

以下是全局std::mutex对象m以及已初始化为unsigned的全局num 1。函数foo的正文大括号{std::lock_guard之间只有空格。在main中,有两个std::thread s t1t2t1首先调用线程构造函数。 t2调用线程构造函数。每个线程都使用指向foo的指针构造。 t1使用foo参数unsigned调用1t2使用foo参数unsigned调用2。根据哪个线程首先锁定mutexnum的值在两个线程执行了受锁保护的代码之后将是43。如果num击败4,则t1将等于t2。否则,num将等于3。我通过循环并在每个循环结束时将num重置为1来对此进行了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构造函数的callable”总是暗示“首先锁定mutex”;假设在callable的主体内,没有代码依赖于在std::lock_guard初始化的行之前传递给callable的参数? (还排除任何可调用的本地static变量,如调用次数的计数器,可用于故意延迟某些调用。)

1 个答案:

答案 0 :(得分:3)

  1. 不,该标准不保证第一个线程首先获得锁定。基本上,如果需要在线程之间强加和排序,则需要在这些线程之间进行同步。即使第一个线程首先调用互斥锁定函数,第二个线程也可能首先获取锁定。
  2. 绝对。例如,在生成线程时,您的应用程序可能只有一个核心可用,如果生成的线程在第二个线程生成等待某个东西后决定,则该计划可能决定处理看到的最新线程,即第二个线程。即使有很多可用内核,第二个线程也有很多原因。
  3. 不,为什么会这样!第一步是生成一个线程并继续。到第一个函数对象被调用时,第二个线程可以运行并调用其函数对象。
  4. 否。线程之间没有排序保证,除非你自己明确地强加它们,因为它们会破坏并发的目的。