Linux线程 -- 互斥锁 和 条件变量

在多线程编程中,互斥量(mutex)是用于保护共享资源的同步机制,确保在任一时刻只有一个线程能够访问共享资源。互斥量用于防止竞态条件(race conditions),确保数据一致性。

基本概念

互斥量(mutex)是一种锁机制,用于实现线程之间的互斥访问。它有两个基本操作:

  1. 锁定(lock):一个线程锁定互斥量以获得对共享资源的独占访问权。如果互斥量已经被其他线程锁定,调用该操作的线程会被阻塞,直到互斥量被解锁。
  2. 解锁(unlock):一个线程解锁互斥量,释放对共享资源的独占访问权,从而允许其他被阻塞的线程获得该资源的访问权。

在Linux中使用互斥量

在Linux中,可以使用POSIX线程库(pthread)中的互斥量。下面是一些主要函数:

  1. 初始化互斥量
    • pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
  2. 销毁互斥量
    • pthread_mutex_destroy(pthread_mutex_t *mutex);
  3. 锁定互斥量
    • pthread_mutex_lock(pthread_mutex_t *mutex);
    • pthread_mutex_trylock(pthread_mutex_t *mutex);
    • pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *abs_timeout);
  4. 解锁互斥量
    • pthread_mutex_unlock(pthread_mutex_t *mutex);

示例代码

以下是一个使用互斥量保护共享资源的简单示例:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

// 互斥锁的声明
pthread_mutex_t mutex;

// 共享变量
int i = 0;

// 线程函数1
void *func1(void *arg)
{
        static char *p = "t1 is run";
        pthread_mutex_lock(&mutex);  // 获取互斥锁
        for(i = 0; i < 5; i++) {
                printf("func1: %d\n", i);
                sleep(1);
        }
        printf("func1: i = %d\n", i);
        pthread_mutex_unlock(&mutex);  // 释放互斥锁
        pthread_exit((void *)p);  // 退出线程并返回信息
}

void *func2(void *arg)
{
        static char *p = "t2 is run";
        while(1){
                if(i == 5){
                        pthread_mutex_lock(&mutex);  // 获取互斥锁
                        printf("fun2: I got mutex\n");
                        printf("fun2=========: %d\n", i);
                        pthread_mutex_unlock(&mutex);  // 释放互斥锁
                        pthread_exit((void *)p);  // 退出线程并返回信息
                }
        }
}

int main()
{
        int param = 100;
        pthread_t t1;
        pthread_t t2;

        // 初始化互斥锁
        pthread_mutex_init(&mutex, NULL);

        char *pret = NULL;
        char *pret2 = NULL;

         // 创建线程1
        int ret = pthread_create(&t1, NULL, func1, (void *)&param);
        if(ret == 0){
                printf("successfully creat a pthread1!\n");
        }
        else{
                printf("faild to creat a pthread\n");
        }

        // 创建线程2
        int ret2 = pthread_create(&t2, NULL, func2, (void *)&param);
        if(ret2 == 0){
                printf("successfully creat a pthread2!\n");
        }
        else{
                printf("faild to creat a pthread\n");
        }

        printf("I am main: %ld\n", (unsigned long)pthread_self());

        // 等待线程1和2结束
        pthread_join(t1, (void **)&pret);
        pthread_join(t2, (void **)&pret2);
        printf("main: t1 quit: %s\n", pret);
        printf("main: t2 quit: %s\n", pret2);
        
         // 销毁互斥锁
        pthread_mutex_destroy(&mutex);

        return 0;
}

运行结果:

解释

  1. 定义互斥量:使用 pthread_mutex_t 类型定义一个互斥量变量 mutex
  2. 初始化互斥量:在使用互斥量之前,需要调用 pthread_mutex_init 函数初始化互斥量。
  3. 锁定和解锁互斥量:在访问共享资源时,使用 pthread_mutex_lockpthread_mutex_unlock 函数锁定和解锁互斥量,确保每次只有一个线程能够访问共享资源。
  4. 销毁互斥量:在不再需要互斥量时,调用 pthread_mutex_destroy 函数销毁互斥量。

注意事项

  1. 避免死锁:确保每个锁定的互斥量最终会被解锁,避免死锁情况的发生。
  2. 正确初始化和销毁:在使用互斥量前一定要初始化,使用后要销毁。
  3. 最小锁定范围:尽量减少锁定的范围,只在必要的代码段中使用锁,以提高并发性。

通过正确使用互斥量,可以确保多线程程序中的共享资源被安全地访问和修改,防止竞态条件的发生。

问题1:什么情况会造成死锁?

在Linux系统中,互斥锁(mutex)用于确保多个线程不会同时访问共享资源。然而,不正确的使用互斥锁可能会导致死锁。最常见的情况如下:

相互持有资源且等待对方释放

这是最经典的死锁场景,通常被称为“循环等待”条件。假设有两个线程,分别持有锁A和锁B:

  • 线程1持有锁A,等待锁B。
  • 线程2持有锁B,等待锁A。

这种情况下,两个线程会无限期地等待对方释放锁,导致死锁。

条件变量

         条件变量(condition variable)是用于线程间协调的一种同步机制。它允许线程在特定条件不满足时等待,并在条件满足时收到通知继续执行。条件变量通常与互斥锁(mutex)结合使用,以确保对共享资源的访问是安全的。

条件变量的主要操作

  1. 初始化和销毁

    • pthread_cond_init: 初始化条件变量。
    • pthread_cond_destroy: 销毁条件变量。
  2. 等待和唤醒

    • pthread_cond_wait: 释放互斥锁并等待条件变量,直到收到信号。
    • pthread_cond_signal: 唤醒一个等待该条件变量的线程。
    • pthread_cond_broadcast: 唤醒所有等待该条件变量的线程。

使用条件变量的步骤

  1. 初始化条件变量和互斥锁
  2. 在条件不满足时等待
  3. 在条件满足时唤醒等待线程
  4. 销毁条件变量和互斥锁

相关函数介绍 

1. pthread_cond_init

功能

初始化条件变量。

原型
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
参数
  • cond: 指向需要初始化的条件变量的指针。
  • attr: 条件变量属性,如果为NULL,使用默认属性。
返回值
  • 成功返回0。
  • 失败返回错误码。

 示例

pthread_cond_t cond;
pthread_cond_init(&cond, NULL);

2. pthread_cond_destroy

功能

销毁条件变量。

原型
int pthread_cond_destroy(pthread_cond_t *cond);
参数
  • cond: 指向需要销毁的条件变量的指针。
返回值
  • 成功返回0。
  • 失败返回错误码。
示例
pthread_cond_destroy(&cond);

3. pthread_cond_wait

功能

等待条件变量。在等待期间,会释放与条件变量关联的互斥锁,并在收到信号后重新获得锁。

原型
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
参数
  • cond: 指向条件变量的指针。
  • mutex: 指向与条件变量关联的互斥锁的指针。
返回值
  • 成功返回0。
  • 失败返回错误码。

示例 

pthread_mutex_lock(&mutex);
while (condition_is_not_met) {
    pthread_cond_wait(&cond, &mutex);
}
pthread_mutex_unlock(&mutex);

4. pthread_cond_signal

功能

发送信号给一个等待该条件变量的线程,唤醒它。

原型
int pthread_cond_signal(pthread_cond_t *cond);
参数
  • cond: 指向条件变量的指针。

示例 

pthread_mutex_lock(&mutex);
condition_is_met = 1;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
返回值
  • 成功返回0。
  • 失败返回错误码。

5. pthread_cond_broadcast

功能

发送信号给所有等待该条件变量的线程,唤醒它们。

原型
int pthread_cond_broadcast(pthread_cond_t *cond);
参数
  • cond: 指向条件变量的指针。
返回值
  • 成功返回0。
  • 失败返回错误码。
示例
pthread_mutex_lock(&mutex);
condition_is_met = 1;
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);

条件变量使用示例

以下是一个示例程序,展示了如何使用条件变量和互斥锁同步两个线程的操作:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

// 定义互斥锁和条件变量
pthread_mutex_t mutex;
pthread_cond_t cond;
int i = 0; // 共享资源

// 线程函数1
void *func1(void *arg) {
    pthread_mutex_lock(&mutex);  // 加锁保护共享资源
    for (i = 0; i < 5; i++) {
        printf("func1: %d\n", i);
        sleep(1);  // 模拟工作
    }
    pthread_cond_signal(&cond);  // 通知等待线程条件已满足
    pthread_mutex_unlock(&mutex);  // 释放锁
    pthread_exit(NULL);  // 退出线程
}

// 线程函数2
void *func2(void *arg) {
    pthread_mutex_lock(&mutex);  // 加锁保护共享资源
    while (i < 5) {  // 当条件不满足时等待
        pthread_cond_wait(&cond, &mutex);  // 等待条件变量的信号
    }
    printf("func2: %d\n", i);
    pthread_mutex_unlock(&mutex);  // 释放锁
    pthread_exit(NULL);  // 退出线程
}

int main() {
    pthread_t t1, t2;

    // 初始化互斥锁和条件变量
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);

    // 创建线程1和线程2
    pthread_create(&t1, NULL, func1, NULL);
    pthread_create(&t2, NULL, func2, NULL);

    // 等待线程1和线程2结束
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

    // 销毁互斥锁和条件变量
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);

    return 0;
}

运行结果如下: 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/678519.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

计网期末复习指南(六):应用层(DNS、FTP、URL、HTTP、SMTP、POP3)

前言&#xff1a;本系列文章旨在通过TCP/IP协议簇自下而上的梳理大致的知识点&#xff0c;从计算机网络体系结构出发到应用层&#xff0c;每一个协议层通过一篇文章进行总结&#xff0c;本系列正在持续更新中... 计网期末复习指南&#xff08;一&#xff09;&#xff1a;计算…

图论:倍增求解最近公共祖先LCA

说明&#xff1a;CSDN和公众号文章同步发布&#xff0c;需要第一时间收到最新内容&#xff0c;请关注公众号【比特正传】。 最近公共祖先LCA是NOI大纲中指定的提高组的图论部分的知识点&#xff0c;难度系数为6&#xff0c;提高组考察难度为5~8。 引入 树是一种特殊的图&…

6 -力扣高频 SQL 50 题(基础版)

6. 使用唯一标识码替换员工ID -- MySQL中的left join是一种连接方式 -- 它以左表为基准&#xff0c;返回左表中所有的行&#xff0c;同时返回右表中与左表匹配的行。 -- 如果右表中没有匹配的行&#xff0c;则用NULL填充。 select unique_id,name from Employees e left join …

告别繁琐,Xinstall一键解决App代理结算难题!

在移动互联网的浪潮中&#xff0c;App的推广和运营成为了众多企业和开发者关注的焦点。然而&#xff0c;随着App市场的日益竞争&#xff0c;代理结算的复杂性和繁琐性成为了许多推广者头疼的问题。为了解决这个问题&#xff0c;Xinstall凭借其专业的技术和丰富的服务经验&#…

项目更换服务器时间少8小时

时区错误 输入 date 查看当前的linux系统时间 hwclock --show 查看当前linux硬件时间 如果发现系统时间和硬件时间不同步&#xff0c;而且硬件时间是正确的&#xff0c;可以用以下命令&#xff1a;hwclock --hctosys 把硬件时间同步到系统时间 mysql时区错误可以参考这位大…

java环境部署

jdk下载 如果你电脑已经下载了 jdk &#xff0c;那你可以跳过这一步了 jdk 的下载路径:https://www.oracle.com/java/technologies/downloads 我这里下载的是java21 你们可以选择自己需要下载的 游览进去的页面是这样子的&#xff08;相比以前这个页面发生很大变化了&#x…

《额尔古纳河右岸》有感

看完了迟子建老师的《额尔古纳河右岸》&#xff0c;老规矩&#xff0c;写点东西吧。最近一段时间确实挺迷茫的&#xff0c;所以给自己找了点事儿&#xff0c;看看书。期初并不能认真看进去&#xff0c;慢慢的看见去之后&#xff0c;就愈发想知道故事的后来。 书里有太多关于死亡…

用增之Firebase

目录 简介 开发准备&#xff1a; 1、在Firebase平台创建项目 2、将项目关联到应用 3、项目配置 简介 前面讲了google ddl部分&#xff0c;本篇为Firebase的事件上报部分&#xff0c;包括在FireBase平台创建应用 &#xff0c; 如果有用到ddl…

如何判断ubuntu是桌面版(destop版)还是服务版(server版)?(systemctl status display-manager)

文章目录 用命令systemctl status display-manager 用命令systemctl status display-manager systemctl status display-manager如果是ubuntu desktop&#xff0c;将显示服务正在运行&#xff0c;如&#xff1a; 如果是ubuntu server&#xff0c;将不会显示服务&#xff0c;提…

《缺失MRI模态下的脑肿瘤分割的潜在相关表示学习》| 文献速递-深度学习肿瘤自动分割

Title 题目 Latent Correlation Representation Learning for Brain Tumor Segmentation With Missing MRI Modalities 《缺失MRI模态下的脑肿瘤分割的潜在相关表示学习》 01 文献速递介绍 脑肿瘤是世界上最具侵略性的癌症之一&#xff0c;脑肿瘤的早期诊断在临床评估和治…

【Javascript系统学习】

语法与数据类型 语法 var\let\const 用 var 或 let 语句声明的变量&#xff0c;如果没有赋初始值&#xff0c;则其值为 undefined。如果访问一个未声明的变量会导致抛出 ReferenceError 异常&#xff1a; undefined 值在布尔类型环境中会被当作 false数值类型环境中 undefin…

APP分发移动应用分发未来:内容驱动

APP分发移动应用分发未来&#xff1a;内容驱动 一、引言 随着移动互联网技术的不断进步和用户需求的日益多样化&#xff0c;移动应用分发行业正面临着前所未有的机遇与挑战。在这个过程中&#xff0c;内容驱动成为了一个重要的趋势&#xff0c;它不仅可以提升用户体验&#x…

香橙派OrangePi AIpro上手笔记——之USB摄像头目标检测方案测试(三)

整期笔记索引 香橙派OrangePi AIpro上手笔记——之USB摄像头目标检测方案测试&#xff08;一&#xff09; 香橙派OrangePi AIpro上手笔记——之USB摄像头目标检测方案测试&#xff08;二&#xff09; 香橙派OrangePi AIpro上手笔记——之USB摄像头目标检测方案测试&#xff08;…

前端之HTML语言之基础标签(持续更新)(基础部分更新结束)

前端之HTML语言 学习完后端的各种层之后&#xff0c;今天开始学习前端&#xff0c;前端和后端都是一个项目的组成部分。 前端对应得到语言是HTML&#xff0c;HTML最重要的有三块&#xff0c;行为&#xff0c;样式&#xff0c;J结构。行为就是交互&#xff0c;理解为鼠标的点击…

Python数据分析案例45——基于融合模型(Stack)的电商用户购买行为预测

案例背景 最近618快到了&#xff0c;上电商购买的人很多&#xff0c;正好我手上还有这个用户购买行为的数据&#xff0c;就做了一个机器学习模型流程&#xff0c;然后也使用的都是常见的机器学习模型&#xff0c;但是加了一点创新吧&#xff0c;使用了stacking融合模型。简单来…

【python】成功解决“NameError: name ‘X’ is not defined”错误的全面指南

成功解决“NameError: name ‘X’ is not defined”错误的全面指南 一、引言 在Python编程中&#xff0c;NameError: name X is not defined是一个常见的错误。这个错误通常意味着我们试图使用一个未定义的变量名X。本文将详细解析这一错误的原因&#xff0c;并提供一系列实用…

正版软件 | Fences 最新版本 V5 - 组织工作流程的最佳方式

『Fences 简介』 Stardock Fences 是一款由 Stardock 公司开发的桌面组织工具&#xff0c;旨在帮助用户管理桌面上的图标和文件。以下是对 Stardock Fences 软件的概述&#xff1a; Stardock Fences 概述 开发商: Stardock 功能: 桌面图标管理: Fences 允许用户将桌面上的…

【Python机器学习】无监督学习——不同类型的预处理

之前学习过&#xff0c;一些算法&#xff08;比如神经网络和SVM&#xff09;对数据缩放非常敏感。因此&#xff0c;通常的做法是对特征进行调节&#xff0c;使数据更适合于这些算法。通常来说&#xff0c;这是对数据的一种简单的按照特征的缩放和移动。举例&#xff1a; impor…

MPLAB--读写MCU数据

空工程 Read –Programmer\Read –File\Export, –确定后选择文件位置 & 文件名 Program –File\Import…,选择烧录的文件*.hex –Programmer\Program

在Vue3中使用WebHQChart实现K线图的沙盘推演

本文由ScriptEcho平台提供技术支持 项目地址&#xff1a;传送门 Vue.js K线沙盘推演代码 应用场景介绍 本代码演示了一个使用 Vue.js 框架开发的 K 线沙盘推演工具&#xff0c;它允许用户加载历史 K 线数据并对其进行编辑和修改&#xff0c;从而模拟和分析不同的市场走势。…