【发布时间】:2014-01-11 01:06:31
【问题描述】:
我很少考虑在两个连续的表达式之间,在调用函数和执行其主体的第一个表达式之间,或者在调用构造函数和执行其初始化程序之间会发生什么。然后我开始阅读有关并发的文章...
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::threads t1 和t2。 t1 首先调用线程构造函数。 t2 第二次调用线程构造函数。每个线程都由一个指向foo 的指针构成。 t1 使用unsigned 参数1 调用foo。 t2 使用 unsigned 参数 2 调用 foo。根据哪个线程首先锁定mutex,num 的值将是4 或3,在两个线程都执行了锁保护代码之后。如果t1 击败t2 锁定,num 将等于4。否则,num 将等于 3。我通过循环和在每个循环结束时将num 重置为1 进行了 100,000 次试验。 (据我所知,结果不也不应该取决于哪个线程首先是join()ed。)
#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”;假设在可调用的主体中,不存在依赖于在 std::lock_guard 初始化行之前传递给可调用的参数的代码? (同时排除任何 callable 的本地 static 变量,如被调用次数的计数器,可用于故意延迟某些调用。)
【问题讨论】:
-
不,是,不清楚,不。
标签: c++ multithreading locking mutex stdthread