Я редко думал о том, что происходит между двумя последовательными выражениями, между вызовом функции и выполнением ее первого выражения или между вызовом конструктора и выполнением его инициализатор. Тогда я начал читать о параллельности ...Порядок блокировки std :: mutex
1.) В течение двух последовательных вызовов конструктора std::thread
«s с тем же отзывной (например, функции, функтора, лямбда), тело которого начинается с инициализации std::lock_guard
с тем же объектом std::mutex
, гарантирует ли стандарт, что поток, соответствующий первому вызову конструктора thread
, сначала выполняет защищенный от блокировки код?
2.) Если стандарт не дает гарантии, то есть ли теоретическая или практическая возможность, что поток, соответствующий второму вызову конструктора thread
, выполняет сначала защищенный код? (Например, тяжелая нагрузка системы во время выполнения инициализатора или органа первого вызова конструктора thread
)
Вот глобальный std::mutex
объекта m
и глобальный unsigned
num
инициализируется 1
. Нет ничего, кроме пробела между функцией foo
's открывающая скобка тела {
и std::lock_guard
. В main
есть два std::thread
s t1
и t2
. t1
сначала вызывает конструктор потока. t2
вызывает второй конструктор потока. Каждый поток создается с указателем на foo
. t1
звонки foo
с unsigned
аргумент 1
. t2
звонки foo
с unsigned
аргумент 2
. В зависимости от того, какая нить блокирует сначала mutex
, значение num
будет либо 4
, либо 3
после того, как оба потока выполнили защищенный от блокировки код. num
будет равно 4
если t1
бьет t2
до замка. В противном случае num
будет равно 3
. Я провел 100 000 испытаний этого путем циклирования и сброса num
до 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;
}
В конце концов, count
равна 100000
, так получается t1
выигрывает гонку каждый время. Но эти испытания ничего не доказывают.
3.) Существует ли стандартный мандат «сначала для вызова thread
« конструктор »всегда подразумевает« сначала вызов вызываемого, переданного в конструктор thread
»?
4.) Предусмотрен ли стандартный мандат «сначала для вызова вызываемого, переданного в конструктор thread
», всегда означает «сначала заблокировать mutex
»; если в теле вызываемого объекта отсутствует код, зависящий от параметра (ов), переданного вызываемому до строки с инициализацией std::lock_guard
? (Также исключаю локальную static
переменной любой вызываемого, как и счетчик числа раз называется, который может быть использован намеренно задерживать определенные вызовы.)
Нет, да, непонятно, нет. –