Как мы знаем, правильное использование std :: lock_guard выглядит следующим образом: RAII style:
void increase_decrease() {
std::lock_guard<std::mutex> guard(global_mutex);
static const int times = 50;
for (int i = 0; i < times; i++) {
global_data ++;
}
for (int i = 0; i < times; i++) {
global_data --;
}
}
Здесь моя точка зрения не о том, какиспользовать std::lock_guard
или мьютекс.
В приведенном ниже коде мы сознательно используем std::lock_guard
неправильно 1015 *.(То есть поместить его в блок перед критической секцией.)
16 создаются потоки, чтобы добавить 1 к 1 и вычесть 1 из глобальной переменной int, которая инициализируется как 0, в 50 раз.
std::lock_guard
вызывается в блоке, а блок находится перед критической секцией ( НЕПРАВИЛЬНО! Никогда не делайте что-то подобное!) .Mutex будет выпущен после блока ( неправильное использование, снова ), следуя механизму RAII .Итак, когда он входит в критическую секцию, там нет блокировки.
#include <iostream>
#include <chrono>
#include <thread>
#include <mutex>
#include <vector>
int global_data = 0;
std::mutex global_mutex;
void increase_decrease() {
// XXX: INCORRECT USAGE! INCORRECT USAGE! INCORRECT USAGE!
{
std::lock_guard<std::mutex> guard(global_mutex);
}
// // XXX: uncomment to sleep for a litter while
// std::this_thread::sleep_for(std::chrono::milliseconds(10));
static const int times = 50;
for (int i = 0; i < times; i++) {
global_data ++;
}
for (int i = 0; i < times; i++) {
global_data --;
}
}
void try_mutex() {
const int num_workers = 16;
std::vector<std::thread> workers;
auto start = std::chrono::system_clock::now();
for (int i = 0; i < num_workers; i++) {
std::thread t(increase_decrease);
workers.push_back(std::move(t));
}
for (auto &t: workers) {
t.join();
}
auto end = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed_seconds = end-start;
std::cout << "global_data: " << global_data
<< ", elapsed second: " << elapsed_seconds.count();
}
int main() {
try_mutex();
}
Я обнаружил, что если сон в течение 10 мс вызывает разные результаты.
Без сна стандартный вывод 20 вызовов main:
global_data: 0, elapsed second: 0.000363
global_data: 0, elapsed second: 0.000359
global_data: 0, elapsed second: 0.000349
global_data: 0, elapsed second: 0.000345
global_data: 0, elapsed second: 0.000352
global_data: 0, elapsed second: 0.000323
global_data: 0, elapsed second: 0.000619
global_data: 0, elapsed second: 0.000431
global_data: 34, elapsed second: 0.000405
global_data: -14, elapsed second: 0.000415
global_data: 0, elapsed second: 0.000497
global_data: 0, elapsed second: 0.000366
global_data: 0, elapsed second: 0.000413
global_data: 0, elapsed second: 0.000406
global_data: 0, elapsed second: 0.000353
global_data: 0, elapsed second: 0.000363
global_data: 0, elapsed second: 0.000361
global_data: 0, elapsed second: 0.000358
global_data: 0, elapsed second: 0.000348
global_data: 0, elapsed second: 0.000367
Однако , если мы раскомментируем сон , стандартный вывод 20 вызовов main:
global_data: 44, elapsed second: 0.011108
global_data: 15, elapsed second: 0.010645
global_data: 25, elapsed second: 0.012905
global_data: 27, elapsed second: 0.012914
global_data: 9, elapsed second: 0.012871
global_data: 46, elapsed second: 0.012836
global_data: 44, elapsed second: 0.011307
global_data: -2, elapsed second: 0.01286
global_data: 77, elapsed second: 0.012853
global_data: 43, elapsed second: 0.011984
global_data: 0, elapsed second: 0.011134
global_data: -3, elapsed second: 0.011571
global_data: 49, elapsed second: 0.012438
global_data: 43, elapsed second: 0.011552
global_data: -20, elapsed second: 0.010807
global_data: 0, elapsed second: 0.010514
global_data: 0, elapsed second: 0.010916
global_data: -44, elapsed second: 0.012829
global_data: 50, elapsed second: 0.011759
global_data: 9, elapsed second: 0.012873
Вероятность того, что global_data
равно 0
, намного больше в первом случае, чем во втором.Я пробовал много раз.Это не просто совпадение.
Итак, похоже, что мьютекс может немного подействовать на после блока, в котором он получил через std::lock_guard
.Зачем?
Спасибо.