【lesson55】线程同步

线程同步

同步:在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,从而有效避免饥饿问题,叫做同步

例一我们之前的抢票代码,一个线程把票全抢完了,它错了吗?没错!但是不合理。

例二:我们要去华为店里买华为Mate40,
第一天去华为专卖店,我们问店员有没有货,店员说没有,我们立马就转身离开。
第二天再去我们问店员有没有货,店员说没有,我们再次转身离开。
就这样连续去30天左右,店里终于有货了,我们买了就走。
上面这样有错吗?没错,但是不合理。

1.例子一频繁的申请到资源
2.例子二太过于浪费我自己和对方的资源(时间)
例子一和二都没有错但是不合理。

例子一造成别人的饥饿问题。

而线程同步主要就是为了解决,访问临界资源合理性的问题。
故事
学校有一间自习室,每天早上8:00~晚上21:00开放,但是这间自习室只允许一个人进入,墙上有一把钥匙,钥匙需要大家抢夺,谁抢到钥匙谁就拥有自习室。

小明某一天早早的来到了自习室,拿了墙上的钥匙,就打开门,然后关上门把门反锁,再把钥匙放在兜里。
自习了几个小时小明准备去趟厕所,打开门发现人有点多,就把门反锁了然后把钥匙一起带走,上完厕所就再回来,自习室就还是属于自己的。
晚上要吃晚饭了,小明准备回去,一打开门发现门口人乌泱泱的,小明立马反悔,觉得还是再学一会吧。
又学了一会小明觉得该回去了,再次打开门,然后把钥匙挂墙上,可是刚挂上去小明又后悔了,而又因为小明里钥匙最近所以他又抢到了钥匙,然后回去再学了一会。

听完上面故事大家觉得合理吗?不合理,小明凭什么一直能抢到钥匙。
有人举报了小明,所以自习室管理员引入规则:
1.自习室外面的人必须排好队,不能乌泱泱的一群人挤在一起
2.人从自习室出来以后不能立马再次申请锁,得必须排到队伍的末尾。

按照一定顺序,进行临界资源的访问我们称之为线程同步。

如何保证顺序性?
条件变量
我们之前申请临界资源前---->都需要做临界资源是否存在的检测---->而检测的本质:也是访问临界资源!---->所以我们之前临界资源的检测也在加锁和解锁之间---->常规方式要检测条件就绪,注定了我们必须频繁申请和释放锁---->有没有办法让我们的线程检测到资源不就绪的时候。
1.不要让线程在频繁的自己检测了,我们等待资源就绪
2.当条件就绪的时候,通知对应的线程,让他来进行资源申请和访问!

之前买华为手机的例子,当店里没有货的时候,我们留下店员的联系方式或者留下自己的联系方式,让店员有货了在通知自己过去买手机。

条件变量:
当一个线程互斥地访问某个变量时,它可能发现在其它线程改变状态之前,它什么也做不了。
例如一个线程访问队列时,发现队列为空,它只能等待,只到其它线程将一个节点添加到队列中。这种情况就需要用到条件变量。

没加条件变量前的代码:

#include <iostream>
#include <string>
#include <pthread.h>
#include <unistd.h>

#define TNUM 4

typedef void (*func)(std::string name);

struct ThreadData
{
public:
    ThreadData(std::string name, func func)
        : _name(name),
          _func(func)
    {
    }

public:
    std::string _name;
    func _func;
};

void fun1(std::string name)
{
    while (true)
    {
        std::cout << name << " running ------- a" << std::endl;
        sleep(1);
    }
}

void fun2(std::string name)
{
    while (true)
    {
        std::cout << name << " running ------- b" << std::endl;
        sleep(1);
    }
}

void fun3(std::string name)
{
    while (true)
    {
        std::cout << name << " running ------- c" << std::endl;
        sleep(1);
    }
}

void fun4(std::string name)
{
    while (true)
    {
        std::cout << name << " running ------- d" << std::endl;
        sleep(1);
    }
}

void *Entry(void *args)
{
    ThreadData *td = (ThreadData *)args;
    td->_func(td->_name);
    delete td;
}

int main()
{
    pthread_t tid[TNUM];
    func func[TNUM] = {fun1, fun2, fun3, fun4};

    for (int i = 0; i < TNUM; i++)
    {
        std::string name = "Thread";
        name += std::to_string(i + 1);
        ThreadData *td = new ThreadData(name, func[i]);
        pthread_create(tid + i, nullptr, Entry, (void *)td);
    }

    for (int i = 0; i < TNUM; i++)
    {
        pthread_join(tid[i], nullptr);
    }
    return 0;
}

运行结果:
在这里插入图片描述
我们看到线程是随机运行的,谁都有可能被CPU调度,那么我们如何让他顺序运行呢?
添加条件变量:
代码:

#include <iostream>
#include <string>
#include <pthread.h>
#include <unistd.h>

#define TNUM 4

typedef void (*func)(std::string name,pthread_mutex_t *mtx, pthread_cond_t *cond);

struct ThreadData
{
public:
    ThreadData(std::string name, func func, pthread_mutex_t *mtx, pthread_cond_t *cond)
        : _name(name),
          _func(func),
          _mtx(mtx),
          _cond(cond)
    {
    }

public:
    std::string _name;
    func _func;
    pthread_mutex_t *_mtx;
    pthread_cond_t *_cond;
};

void fun1(std::string name,pthread_mutex_t *mtx, pthread_cond_t *cond)
{
    while (true)
    {
        pthread_cond_wait(cond,mtx);
        std::cout << name << " running ------- a" << std::endl;
        //sleep(1);
    }
}

void fun2(std::string name,pthread_mutex_t *mtx, pthread_cond_t *cond)
{
    while (true)
    {
        pthread_cond_wait(cond,mtx);
        std::cout << name << " running ------- b" << std::endl;
        //sleep(1);
    }
}

void fun3(std::string name,pthread_mutex_t *mtx, pthread_cond_t *cond)
{
    while (true)
    {
        pthread_cond_wait(cond,mtx);
        std::cout << name << " running ------- c" << std::endl;
        //sleep(1);
    }
}

void fun4(std::string name,pthread_mutex_t *mtx, pthread_cond_t *cond)
{
    while (true)
    {
        pthread_cond_wait(cond,mtx);
        std::cout << name << " running ------- d" << std::endl;
        //sleep(1);
    }
}

void *Entry(void *args)
{
    ThreadData *td = (ThreadData *)args;
    td->_func(td->_name,td->_mtx,td->_cond);
    delete td;

    return nullptr;
}

int main()
{
    pthread_mutex_t mtx;
    pthread_cond_t cond;

    pthread_mutex_init(&mtx, nullptr);
    pthread_cond_init(&cond, nullptr);

    pthread_t tid[TNUM];
    func func[TNUM] = {fun1, fun2, fun3, fun4};

    for (int i = 0; i < TNUM; i++)
    {
        std::string name = "Thread";
        name += std::to_string(i + 1);
        ThreadData *td = new ThreadData(name, func[i], &mtx, &cond);
        pthread_create(tid + i, nullptr, Entry, (void *)td);
    }

    while(true)
    {
        std::cout << "Wake up thread run code ......." << std::endl;
        pthread_cond_signal(&cond);
        sleep(1);
    }

    for (int i = 0; i < TNUM; i++)
    {
        pthread_join(tid[i], nullptr);
    }

    pthread_mutex_destroy(&mtx);
    pthread_cond_destroy(&cond);
    return 0;
}

运行结果:
在这里插入图片描述
我们看到线程变的井然有序,一个一个的执行。
在这里插入图片描述
pthread_cond_signal是一个一个的唤醒线程,我们还可以用pthread_cond_broadcast一批一批的唤醒。
修改代码片段
在这里插入图片描述
运行代码:
在这里插入图片描述
如果我们想让线程运行10次就退出,那么代码该如何更改呢?
我们在全局定义一个bool类型的quit。然后每个线程用quit判断退出条件即可。
代码:

#include <iostream>
#include <string>
#include <pthread.h>
#include <unistd.h>

#define TNUM 4

bool quit = false;

typedef void (*func)(std::string name,pthread_mutex_t *mtx, pthread_cond_t *cond);

struct ThreadData
{
public:
    ThreadData(std::string name, func func, pthread_mutex_t *mtx, pthread_cond_t *cond)
        : _name(name),
          _func(func),
          _mtx(mtx),
          _cond(cond)
    {
    }

public:
    std::string _name;
    func _func;
    pthread_mutex_t *_mtx;
    pthread_cond_t *_cond;
};

void fun1(std::string name,pthread_mutex_t *mtx, pthread_cond_t *cond)
{
    while (!quit)
    {
        pthread_cond_wait(cond,mtx);
        std::cout << name << " running ------- a" << std::endl;
        //sleep(1);
    }
}

void fun2(std::string name,pthread_mutex_t *mtx, pthread_cond_t *cond)
{
    while (!quit)
    {
        pthread_cond_wait(cond,mtx);
        std::cout << name << " running ------- b" << std::endl;
        //sleep(1);
    }
}

void fun3(std::string name,pthread_mutex_t *mtx, pthread_cond_t *cond)
{
    while (!quit)
    {
        pthread_cond_wait(cond,mtx);
        std::cout << name << " running ------- c" << std::endl;
        //sleep(1);
    }
}

void fun4(std::string name,pthread_mutex_t *mtx, pthread_cond_t *cond)
{
    while (!quit)
    {
        pthread_cond_wait(cond,mtx);
        std::cout << name << " running ------- d" << std::endl;
        //sleep(1);
    }
}

void *Entry(void *args)
{
    ThreadData *td = (ThreadData *)args;
    td->_func(td->_name,td->_mtx,td->_cond);
    delete td;

    return nullptr;
}

int main()
{
    pthread_mutex_t mtx;
    pthread_cond_t cond;

    pthread_mutex_init(&mtx, nullptr);
    pthread_cond_init(&cond, nullptr);

    pthread_t tid[TNUM];
    func func[TNUM] = {fun1, fun2, fun3, fun4};

    for (int i = 0; i < TNUM; i++)
    {
        std::string name = "Thread";
        name += std::to_string(i + 1);
        ThreadData *td = new ThreadData(name, func[i], &mtx, &cond);
        pthread_create(tid + i, nullptr, Entry, (void *)td);
    }

    int cnt = 10;
    while(cnt)
    {
        std::cout << std::endl;
        std::cout << "Wake up thread run code ......." <<  cnt-- << std::endl;
        //pthread_cond_signal(&cond);
        pthread_cond_broadcast(&cond);
        sleep(1);
    }

    quit = true;

    for (int i = 0; i < TNUM; i++)
    {
        pthread_join(tid[i], nullptr);
    }

    pthread_mutex_destroy(&mtx);
    pthread_cond_destroy(&cond);
    return 0;
}

运行结果:
在这里插入图片描述
我们看到这最后卡住了是为什么呢?因为我们虽然是quit但是,所有线程还在条件变量上等待呢,而条件变量是在加锁和解锁之间的,我们虽然退出了但是锁并没有处理好。
所以我们在每个线程都要加上加锁解锁的逻辑。
代码:

#include <iostream>
#include <string>
#include <pthread.h>
#include <unistd.h>

#define TNUM 4

bool quit = false;

typedef void (*func)(std::string name,pthread_mutex_t *mtx, pthread_cond_t *cond);

struct ThreadData
{
public:
    ThreadData(std::string name, func func, pthread_mutex_t *mtx, pthread_cond_t *cond)
        : _name(name),
          _func(func),
          _mtx(mtx),
          _cond(cond)
    {
    }

public:
    std::string _name;
    func _func;
    pthread_mutex_t *_mtx;
    pthread_cond_t *_cond;
};

void fun1(std::string name,pthread_mutex_t *mtx, pthread_cond_t *cond)
{
    while (!quit)
    {
        pthread_mutex_lock(mtx);
        pthread_cond_wait(cond,mtx);
        std::cout << name << " running ------- a" << std::endl;
        pthread_mutex_unlock(mtx);
        //sleep(1);
    }
}

void fun2(std::string name,pthread_mutex_t *mtx, pthread_cond_t *cond)
{
    while (!quit)
    {
        pthread_mutex_lock(mtx);
        pthread_cond_wait(cond,mtx);
        std::cout << name << " running ------- b" << std::endl;
        pthread_mutex_unlock(mtx);
        //sleep(1);
    }
}

void fun3(std::string name,pthread_mutex_t *mtx, pthread_cond_t *cond)
{
    while (!quit)
    {
        pthread_mutex_lock(mtx);
        pthread_cond_wait(cond,mtx);
        std::cout << name << " running ------- c" << std::endl;
        pthread_mutex_unlock(mtx);
        //sleep(1);
    }
}

void fun4(std::string name,pthread_mutex_t *mtx, pthread_cond_t *cond)
{
    while (!quit)
    {
        pthread_mutex_lock(mtx);
        pthread_cond_wait(cond,mtx);
        std::cout << name << " running ------- d" << std::endl;
        pthread_mutex_unlock(mtx);
        //sleep(1);
    }
}

void *Entry(void *args)
{
    ThreadData *td = (ThreadData *)args;
    td->_func(td->_name,td->_mtx,td->_cond);
    delete td;

    return nullptr;
}

int main()
{
    pthread_mutex_t mtx;
    pthread_cond_t cond;

    pthread_mutex_init(&mtx, nullptr);
    pthread_cond_init(&cond, nullptr);

    pthread_t tid[TNUM];
    func func[TNUM] = {fun1, fun2, fun3, fun4};

    for (int i = 0; i < TNUM; i++)
    {
        std::string name = "Thread";
        name += std::to_string(i + 1);
        ThreadData *td = new ThreadData(name, func[i], &mtx, &cond);
        pthread_create(tid + i, nullptr, Entry, (void *)td);
    }

    int cnt = 10;
    while(cnt)
    {
        std::cout << std::endl;
        std::cout << "Wake up thread run code ......." <<  cnt-- << std::endl;
        //pthread_cond_signal(&cond);
        pthread_cond_broadcast(&cond);
        sleep(1);
    }
    
    quit = true;
   

    for (int i = 0; i < TNUM; i++)
    {
        pthread_join(tid[i], nullptr);
    }

    pthread_mutex_destroy(&mtx);
    pthread_cond_destroy(&cond);
    return 0;
}

运行结果:
在这里插入图片描述
我们看到这最后卡住了是为什么呢?因为我们虽然是quit但是,所有线程还在条件变量上等待呢,所以我们需要在退出后再进行一次pthread_cond_broadcast();
在这里插入图片描述
运行结果:
在这里插入图片描述

  • 33
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值