当线程间共享非const类型的数据时,线程管理的最大挑战之一就开始了。。
使用共享数据的线程的上下文中,您经常会听到竞争条件和竞争区。
但是,那是啥?
数据竞争
数据竞争是一种状态,其中至少两个线程同时访问共享数据,并且至少一个线程是写入者。
竞争区
竞争区是代码的一部分,在任何时间点都不应该有多个线程访问。
如果程序具有竞争条件,则程序行为未定义。换句话说,任何事情都可能发生。
查看竞争条件的一种好方法是让几个线程写入std::cout。
std::cout是共享对象(输出流),应该保护它不受多个线程的同时访问:
// coutUnsynchronized.cpp
#include <chrono>
#include <iostream>
#include <thread>
class Worker
{
public:
Worker(std::string n) :
name(n)
{};
void operator() ()
{
for (int i = 1; i <= 3; ++i)
{
// begin work
std::this_thread::sleep_for(std::chrono::milliseconds(200));
// end work
std::cout << name << ": " << "Work " << i << " done !!!" << std::endl;
}
}
private:
std::string name;
};
int main()
{
std::cout << std::endl;
std::cout << "Boss: Let's start working.\n\n";
std::thread herb = std::thread(Worker("Herb"));
std::thread andrei = std::thread(Worker(" Andrei"));
std::thread scott = std::thread(Worker(" Scott"));
std::thread bjarne = std::thread(Worker(" Bjarne"));
std::thread andrew = std::thread(Worker(" Andrew"));
std::thread david = std::thread(Worker(" David"));
herb.join();
andrei.join();
scott.join();
bjarne.join();
andrew.join();
david.join();
std::cout << "\n" << "Boss: Let's go home." << std::endl;
std::cout << std::endl;
}
每个线程打印三句话,执行完毕后join,但是打印出来。。。。一团糟:
再试一次还是一团糟。。。。完全不受控制:
互斥锁
有一种解决方案是互斥锁。
互斥锁确保每个线程专门访问共享变量std::cout。
Mutex代表mutual exclusion。它确保只有一个线程可以访问关键部分
注意:
std::cout是线程安全的
C ++ 11标准保证,您不能保护写入std::cout的单个字符。
每个字符都将以原子方式书写。
当然,有可能的是,例如示例中的更多输出语句将交错,但这只是一个视觉的问题。
这方面对所有输入和输出流都有效。
// coutSynchronized.cpp
#include <chrono>
#include <iostream>
#include <mutex>
#include <thread>
std::mutex coutMutex;
class Worker
{
public:
Worker(std::string n) :name(n) {};
void operator() ()
{
for (int i = 1; i <= 3; ++i) {
// begin work
std::this_thread::sleep_for(std::chrono::milliseconds(200));
// end work
coutMutex.lock();
std::cout << name << ": " << "Work " << i << " done !!!" << std::endl;
coutMutex.unlock();
}
}
private:
std::string name;
};
int main()
{
std::cout << std::endl;
std::cout << "Boss: Let's start working." << "\n\n";
std::thread herb = std::thread(Worker("Herb"));
std::thread andrei = std::thread(Worker(" Andrei"));
std::thread scott = std::thread(Worker(" Scott"));
std::thread bjarne = std::thread(Worker(" Bjarne"));
std::thread andrew = std::thread(Worker(" Andrew"));
std::thread david = std::thread(Worker(" David"));
herb.join();
andrei.join();
scott.join();
bjarne.join();
andrew.join();
david.join();
std::cout << "\n" << "Boss: Let's go home." << std::endl;
std::cout << std::endl;
}
与第一个代码例子的主要区别在于通过调用方法coutMutex.lock()和coutMutex.unlock(),可以定义独占部分,这部分最多只能由一个线程访问。
这样对std::cout的访问是同步的,混乱从此变得和谐。。。。。:
原文地址:
http://www.modernescpp.com/index.php/threads-sharing-data