1

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

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

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

这是一个全局std::mutex对象m和一个unsigned num初始化为1. 函数foo主体的左大括号{std::lock_guard. 在main中,有两个std::threadst1t2t1首先调用线程构造函数。t2第二次调用线程构造函数。每个线程都由一个指向foo. 带参数的t1调用。带参数的调用。根据哪个线程锁定第一个,在两个线程都执行了锁保护代码之后,' 的值将是 a或 a 。将等于foounsigned1t2foounsigned2mutexnum43num4如果t1击败t2锁定。否则,num将等于3。通过循环并在每个循环结束时重置num为,我对此进行了 100,000 次试验。1(据我所知,结果不也不应该取决于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;
}

最后,countequals 100000,所以t1每次都赢得比赛。但这些试验并不能证明什么。

3.) 标准要求“首先调用thread构造函数”是否总是意味着“首先调用传递给thread构造函数的可调用对象”?

4.) 标准要求“首先调用传递给thread构造函数的可调用对象”是否总是意味着“首先锁定mutex”;假设在可调用的主体中,不存在依赖于在std::lock_guard初始化行之前传递给可调用的参数的代码吗?(还排除任何可调用的局部static变量,例如调用次数计数器,可用于故意延迟某些调用。)

4

1 回答 1

3
  1. 不,标准不保证第一个线程首先获得锁。基本上,如果您需要在线程之间强加和排序,则需要在这些线程之间进行同步。即使第一个线程先调用互斥锁函数,第二个线程也可能先获取锁。
  2. 绝对地。例如,在产生线程时,您的应用程序可能只有一个内核可用,如果产生线程在产生第二个线程之后决定等待某事,则调度可能决定处理看到的最新线程,即第二个线程。即使有很多可用的内核,也有很多原因第二个线程更快。
  3. 不,为什么会这样!第一步是产生一个线程并继续。当第一个函数对象被调用时,第二个线程可以运行并调用它的函数对象。
  4. 不,线程之间没有顺序保证,除非您自己明确地强加它们,因为它们会破坏并发性的目的。
于 2014-01-11T01:23:53.483 回答