当前位置: 首页 > news >正文

<Linux线程同步>——《Linux》

 目录

1. Linux线程同步

1.1条件变量

1.2同步概念与竞态条件

1.3条件变量函数

1.4 为什么pthread_ cond_ wait 需要互斥量?

1.5 条件变量使用规范

后记:●由于作者水平有限,文章难免存在谬误之处,敬请读者斧正,俚语成篇,恳望指教!

                                                                           ——By 作者:新晓·故知


1. Linux线程同步

1.1条件变量

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

1.2同步概念与竞态条件

  • 同步:在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,从而有效避免饥饿问题,叫做同步
  • 竞态条件:因为时序问题,而导致程序异常,我们称之为竞态条件。在线程场景下,这种问题也不难理解

1.3条件变量函数

初始化
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t*restrictattr);
参数:
 cond:要初始化的条件变量
 attr:NULL
销毁
int pthread_cond_destroy(pthread_cond_t *cond)
等待条件满足
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
 参数:
 cond:要在这个条件变量上等待
 mutex:互斥量,后面详细解释
唤醒等待
 int pthread_cond_broadcast(pthread_cond_t *cond);
 int pthread_cond_signal(pthread_cond_t *cond);

 


#include <iostream>
#include <vector>
#include <string>
#include <functional>
#include <unistd.h>
#include <pthread.h>
using namespace std;

// 定义一个条件变量
pthread_cond_t cond;
// 定义一个互斥锁
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //当前不用,但是接口需要,所以我们需要留下来
vector<function<void()>> funcs;
void show()
{
    cout << "hello show" << endl;
}
void print()
{
    cout << "hello print" << endl;
}
// 定义全局退出变量
volatile bool quit = false;
void *waitCommand(void *args)
{
    pthread_detach(pthread_self());
    while (!quit)
    {
        // 执行了下面的代码,证明某一种条件不就绪(现在还没有场景),要我这个线程等待
        // 三个线程,都会进在条件变量下进行排队
        pthread_cond_wait(&cond, &mutex); //让对应的线程进行等待,等待被唤醒
        for(auto &f : funcs)
        {
            f();
        }
    }
    cout << "thread id: " << pthread_self() << " end..." << endl;
    return nullptr;
}

int main()
{
    funcs.push_back(show);
    funcs.push_back(print);
    funcs.push_back([](){
        cout << "你好世界!" << endl;
    });
    pthread_cond_init(&cond, nullptr);
    pthread_t t1, t2, t3;
    pthread_create(&t1, nullptr, waitCommand, nullptr);
    pthread_create(&t2, nullptr, waitCommand, nullptr);
    pthread_create(&t3, nullptr, waitCommand, nullptr);

    while(true)
    {
        char n = 'a';
        cout << "请输入你的command(n/q): ";
        cin >> n;
        if(n == 'n') pthread_cond_signal(&cond);
        else break;
    }
    //sleep(1);
    cout << "main thread quit!" << endl;
    pthread_cond_destroy(&cond);
    return 0;
}

 

简单案例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
pthread_cond_t cond;
pthread_mutex_t mutex;
void *r1(void *arg)
{
    while (1)
    {
        pthread_cond_wait(&cond, &mutex);
        printf("活动\n");
    }
}
void *r2(void *arg)
{
    while (1)
    {
        pthread_cond_signal(&cond);
        sleep(1);
    }
}
int main(void)
{
    pthread_t t1, t2;
    pthread_cond_init(&cond, NULL);
    pthread_mutex_init(&mutex, NULL);
    pthread_create(&t1, NULL, r1, NULL);
    pthread_create(&t2, NULL, r2, NULL);
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
}

 

 

1.4 为什么pthread_ cond_ wait 需要互斥量?

  • 条件等待是线程间同步的一种手段,如果只有一个线程,条件不满足,一直等下去都不会满足,所以必须要有一个线程通过某些操作,改变共享变量,使原先不满足的条件变得满足,并且友好的通知等待在条件变量上的线程。
  • 条件不会无缘无故的突然变得满足了,必然会牵扯到共享数据的变化。所以一定要用互斥锁来保护。没有互斥锁就无法安全的获取和修改共享数据。

 

按照上面的说法,我们设计出如下的代码:先上锁,发现条件不满足,解锁,然后等待在条件变量上不 就行了,如下代码 :
// 错误的设计
pthread_mutex_lock(&mutex);
while (condition_is_false)
{
    pthread_mutex_unlock(&mutex);
      //解锁之后,等待之前,条件可能已经满足,信号已经发出,但是该信号可能被错过
        pthread_cond_wait(&cond);
    pthread_mutex_lock(&mutex);
}
pthread_mutex_unlock(&mutex);
  • 由于解锁和等待不是原子操作。调用解锁之后,pthread_ cond_ wait之前,如果已经有其他线程获取到互斥量,摒弃条件满足,发送了信号,那么pthread_ cond_ wait将错过这个信号,可能会导致线程永远阻塞在这个 pthread_ cond_ wait 。所以解锁和等待必须是一个原子操作。
  • int pthread_cond_wait(pthread_cond_ t *cond,pthread_mutex_ t * mutex); 进入该函数后,会去看条件量等于0不?等于,就把互斥量变成1,直到cond_ wait返回,把条件量改成1,把互斥量恢复成原样。

1.5 条件变量使用规范

  • 等待条件代码
 pthread_mutex_lock(&mutex);
 while (条件为假)
 pthread_cond_wait(cond, mutex);
 修改条件
 pthread_mutex_unlock(&mutex);
  • 给条件发送信号代码
pthread_mutex_lock(&mutex);
 设置条件为真
 pthread_cond_signal(cond);
 pthread_mutex_unlock(&mutex);

后记:
●由于作者水平有限,文章难免存在谬误之处,敬请读者斧正,俚语成篇,恳望指教!

                                                                           ——By 作者:新晓·故知

 

相关文章:

  • 【Array数组】面试前基础知识点深度记忆总结
  • 20221226编译Toybrick的TB-RK3588X开发板的Android12系统1-编译环境配置
  • 大话JMeter2|正确get参数传递和HTTP如何正确使用
  • 在Makefile中使用空格缩进的方法
  • 详解vue中vuex的用法
  • 利用Bat打开exe程序并传入值
  • 【iMessage苹果推群发】苹果相册推它由pushchatkey.pem和pushchatcert.pem作为单独的文件使用
  • .NET(C#、VB)APP开发——Smobiler平台控件介绍:Bluetooth组件
  • 基于Xlinx的时序分析与约束(5)----衍生时钟约束
  • Python常见问题整理
  • Docker安装Zookeeper教程(超详细)
  • 【学习笔记12.25】动态规划入门
  • C语言用好写好头文件
  • 程序员过圣诞 | 用HTML写出绽放的烟花
  • 源码系列 之 HashMap
  • [rust! #004] [译] Rust 的内置 Traits, 使用场景, 方式, 和原因
  • [译]如何构建服务器端web组件,为何要构建?
  • javascript 哈希表
  • mysql innodb 索引使用指南
  • Spring框架之我见(三)——IOC、AOP
  • Vue 动态创建 component
  • Vue2 SSR 的优化之旅
  • win10下安装mysql5.7
  • 开源中国专访:Chameleon原理首发,其它跨多端统一框架都是假的?
  • 力扣(LeetCode)965
  • 聊聊redis的数据结构的应用
  • 它承受着该等级不该有的简单, leetcode 564 寻找最近的回文数
  • 我这样减少了26.5M Java内存!
  • ​iOS安全加固方法及实现
  • # Maven错误Error executing Maven
  • (done) ROC曲线 和 AUC值 分别是什么?
  • (Matlab)基于蝙蝠算法实现电力系统经济调度
  • (更新)A股上市公司华证ESG评级得分稳健性校验ESG得分年均值中位数(2009-2023年.12)
  • (译) 理解 Elixir 中的宏 Macro, 第四部分:深入化
  • ***测试-HTTP方法
  • .“空心村”成因分析及解决对策122344
  • .NET基础篇——反射的奥妙
  • .Net中wcf服务生成及调用
  • @NestedConfigurationProperty 注解用法
  • [20160807][系统设计的三次迭代]
  • [ai笔记4] 将AI工具场景化,应用于生活和工作
  • [Android] 修改设备访问权限
  • [Android]Android P(9) WIFI学习笔记 - 扫描 (1)
  • [C/C++]关于C++11中的std::move和std::forward
  • [CQOI 2010]扑克牌
  • [JS] 常用正则表达式集(一)
  • [LeetCode]—Implement strStr() 寻找子串匹配第一个位置 (KMP)
  • [PHP] 代码重用与函数
  • [UE4]GameInstance初始化
  • [第二章—Spring MVC的高级技术] 2.2 置multipart解析器
  • [工业自动化-10]:西门子S7-15xxx编程 - PLC主站 - 信号量:数字量
  • [蓝桥杯复盘] 第 3 场双周赛20231111
  • [领域]从业务到抽象,再到业务 (1)
  • [论文阅读]4DRadarSLAM: A 4D Imaging Radar SLAM System for Large-scale Environments
  • [前端]开启VUE之路-NODE.js版本管理