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
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值