线程的同步互斥

 互斥

互斥保证了在一个时间内只有一个线程访问一个资源。

先看一段代码:三个线程同时对全局变量val进行--,同时val每自减一次其线程局部存储的全局变量++

#include <iostream>
#include <thread>
#include <vector>
#include <unistd.h>
#define NUM 3//线程数量
using namespace std;

int val = 10;
__thread int t_val = 0;

void fun(string name)
{
   while(1)
   {
        if(val > 0)
        {
            usleep(10000);
            cout << name << " get one" << " total: " << t_val << " val: " << val <<endl;
            val--;
            t_val++;
        }
        else 
        {
            break;
        }
   }

}

int main()
{
    thread trd[NUM];
    //创建多个线程
    for(int i = 0; i < NUM; i++)
    {
        string name = "thread" + to_string(i+1);
        trd[i] = thread(fun, name);
    }
    //等待线程
    for(int i = 0; i < NUM; i++)
    {
        trd[i].join();
    }
    return 0;
}

最终的结果val竟然出现了负数,为什么呢?

因为线程函数的操作是非原子的,线程可能在任何一步被切换。这就会导致数据不一致问题,要想解决就必须使用锁。

锁——mutex

mutex是无参构造, 不支持拷贝构造。

两个无参接口,用来加锁和解锁 

#include <iostream>
#include <unistd.h>
#include <thread>
#include <vector>
#include <mutex>
#define NUM 3 // 线程数量
using namespace std;

int val = 10;
thread_local int t_val = 0;
mutex mut;

void fun(string name)
{
    while (1)
    {
        {
            mut.lock();
            if (val > 0)
            {
                usleep(10000);
                t_val++;
                val--;
                cout << name << " get one" << " total: " << t_val << " val: " << val << endl;
                 mut.unlock();
            }
            else
            {
                 mut.unlock();
                break;
            }
        }
        usleep(2);
    }
}

int main()
{
    thread trd[NUM];
    // 创建多个线程
    for (int i = 0; i < NUM; i++)
    {
        string name = "thread" + to_string(i + 1);
        trd[i] = thread(fun, name);
    }
    // 等待线程
    for (int i = 0; i < NUM; i++)
    {
        trd[i].join();
    }
    return 0;
}

加锁保证锁内资源的数据安全性,是一种用时间换取安全的方式。锁在任何时候只能被一个线程申请,那么临界资源就只能被一个线程使用和修改,保证了数据一致性。

锁的分配也要合理,不然会导致线程的饥饿问题。如果线程在释放锁后立马申请锁就会导致其他线程无法获取锁

锁的原理

lock不是原子的,先将立即数0放到寄存器中,再将寄存器内容和内存中的mutex内容交换,最后判断寄存器内容。虽然lock不是原子的,不过即使发生进程切换,也不会发生错误。

锁未被申请时,mutex值为1。被申请后会将1传递到寄存器中,所以锁就是一种传递,哪个线程传递1到寄存器就可以执行

unlock是原子的,将mutex值置为1。

同步

同步可以在数据安全的情况下,让线程按照特定的顺序访问临界资源。

条件变量——condition_variable

 wait 可以让线程停止并等待通知,直到其他线程用notify接口发出通知,线程才会被唤醒。

使用 wait 必须搭配 std::unique_lock<std::mutex> 一起使用 

notify_one:通知一个正在等待的线程 

notify_all:通知所有正在等待的线程

例子:

通过等待,判断临界资源是否准备就绪,决定线程是否继续执行。

#include <iostream>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <unistd.h>

using namespace std;
mutex m;
condition_variable cond;
bool flag = false;

void fun()
{
    unique_lock<mutex> ul(m);
    cout << "已加锁,即将等待" << endl;
    while(flag != true)
    {
        cond.wait(ul);
        cout << "结束等待,判断flag条件" << endl;
    }
    cout << "执行任务。。。" << endl;
}

int main()
{
    thread th(fun);
    sleep(1);
    cond.notify_one();

    sleep(1);
    flag = true;
    cond.notify_one();
    
    th.join();
    cout << "end" << endl;
    return 0;
}

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值