C++多线程编程(二):使用互斥锁

欢迎访问我的个人博客

https://vincillau.github.io/

C++多线程编程(二):使用互斥锁

上回书说到,我们可以用C++11中的thread类来创建和管理线程。在多线程编程中,我们常常遇到对线程间共享数据访问的各种线程安全问题。在这篇博文中我将向大家介绍如何利用C++11提供的<mutex>头文件提供的相关工具来保护线程间共享数据。

竞争条件

首先来看一段代码:

#include <iostream>
#include <thread>

using namespace std;

int n = 0;

void func(void)
{
   
    for (int i = 0; i < 10000; i++)
    {
   
        int x = n;
        n++;
        if (x + 1 == n)
        {
   
            n--;
        }
    }
}

int main(void)
{
   
    thread t1(func);
    thread t2(func);
    thread t3(func);

    t1.join();
    t2.join();
    t3.join();

    cout << n << endl;

    return 0;
}

读者不妨先猜测一下输出结果。

一种可能的猜测:

在函数func中,我们先把n赋值给x,然后让n递增1。按理说,表达式x + 1 == n的结果应该为true,那么n又会递减1。也就是说,执行一次循环,n的值应该不会改变。所以程序最终的输出结果应该是0。
几句就
我们运行一下这个程序看一下结果:

548 // 第一次运行结果

96 // 第二次运行结果

162 // 第三次运行结果

肿么肥四?为什么每次运行结果都不一致?要想搞清楚这样的结果的原因,首先要从线程调度说起:

操作系统在调度线程时采用的是抢占式调度的方式,也就是说,每一个线程在执行一段时间后会被操作系统中断,然后调用另一个线程。操作系统对线程的调度几乎是随机的。在上面的例子中,当我们的线程在执行n++后,操作系统有可能会终端当前线程去执行另一个线程。而另一个线程如果恰好也执行了n++x + 1 == n就不为true了,所以结果也就不是预期的0了。这就是多线程编程中的竞争条件(也叫竞争冒险竞态条件)

那么如果我们想让结果为0需要怎么做呢?这就需要C++中的mutex来保护循环中的代码,让程序中的三个线程在同一时间只有一个线程执行循环中的代码,来避免竞争条件

mutex类

用途

mutex常被称作互斥锁互斥量,位于<mutex>头文件中。mutex的用途就是对可能出现竞争条件的代码段(临界区)“加锁”。线程要进入临界区,首先要获取锁,如果成功获取锁,线程可以进入临界区执行代码。如果线程想要获取的锁已经被其他线程占用,则线程会阻塞,直至其他线程释放这个锁。

创建mutex对象

mutex类只有一个默认构造函数,它会创建一个没有被加锁的mutex对象。mutex类没有拷贝和移动构造函数,所以mutex不能被拷贝或者移动

锁定mutex

线程调用mutex对象的lock成员函数会尝试获取这个锁。如果这个mutex对象没有被其他线程占有,当前线程就会获取这个锁。如果这个mutex已经被其他线程占用,调用线程会被阻塞直到其他线程释放这个锁。

释放mutex

当线程执行完临界区的代码后,应当释放锁以便让其他线程能够获取这个锁。释放mutex通过调用mutex对象的unlock成员函数。

使用mutex的栗子

下面的代码利用mutex改写了上一节的例子:

#include <iostream>
#include <mutex>
#include <thread>

using namespace std;

int
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值