C++线程间共享数据之互斥锁

当线程间共享非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

猜你喜欢

转载自blog.csdn.net/y396397735/article/details/80993738
今日推荐