Linux线程 分离和同步与互斥 条件变量

Linux线程 分离和同步与互斥 条件变量

  • 1. 分离线程
  • 2. 线程互斥与互斥量
  • 3. 线程同步与竞态条件
  • 4. pthread库与条件变量
  • 5. 生产者-消费者

1. 分离线程

  1. 什么是线程分离?
    线程分离是指线程在结束时,操作系统会自动回收其资源,而无需其他线程显式地等待它的结束或调用pthread_join函数。这种机制允许主线程不必关心子线程的状态,从而提高程序的并发性和可维护性。

  2. pthread_detach函数
    pthread_detach 函数是一个用于将线程设置为分离状态的POSIX线程库函数。通过调用这个函数,线程的资源将在其终止时自动回收,无需其他线程调用 pthread_join 来等待该线程的结束。

#include <pthread.h>
int pthread_detach(pthread_t thread);

参数说明
thread:要设置为分离状态的线程标识符
返回值
成功:返回0。
失败:返回错误码,可通过 perror 函数打印出错误信息。

代码示例:

#include <pthread.h>
#include <stdio.h>
#define NUM_THREADS 3// 线程执行的任务
void *thread_function(void *arg) {int thread_id = *((int *)arg);int n = 5;// 模拟线程执行任务,循环5次while (n--) {printf("线程 %d 正在运行\n", thread_id);sleep(1);  // 每秒打印一次,模拟线程执行任务}// 线程结束pthread_exit(NULL);
}int main() {pthread_t threads[NUM_THREADS];int thread_ids[NUM_THREADS];// 创建并启动3个线程for (int i = 0; i < NUM_THREADS; ++i) {thread_ids[i] = i + 1;// 创建线程if (pthread_create(&threads[i], NULL, thread_function, (void *)&thread_ids[i]) != 0) {perror("创建线程失败");return -1;}// 设置线程为分离状态if (pthread_detach(threads[i]) != 0) {perror("设置线程为分离状态失败");return -1;}}// 主线程执行的任务while (1) {printf("主线程正在运行\n");sleep(1);  // 主线程每秒打印一次,模拟执行其他任务}return 0;
}

运行时,使用 while :; do ps -aL ;sleep 1;done 实时查看。
在这里插入图片描述

这个程序创建了3个线程,每个线程模拟执行任务5次。主线程在一个无限循环中每秒打印一次消息,模拟执行其他任务。这里使用了 pthread_create 来创建线程,pthread_detach 将线程设置为分离状态,确保线程在结束时能够自动释放资源。

2. 线程互斥与互斥量

多线程编程是一种广泛应用于提高程序性能的技术。然而,当多个线程同时访问和修改共享资源时,可能会导致数据竞争(Data Race)和其他并发问题。为了解决这些问题,互斥量(Mutex)被引入,成为多线程编程中的重要工具。

  1. 为什么需要互斥量?
    在多线程环境下,多个线程可能同时访问和修改共享资源,导致竞态条件。竞态条件是一种并发问题,可能引起不确定的行为。为了解决这些问题,我们引入互斥量来保护共享资源,确保一次只有一个线程能够访问它

  2. 临界资源与临界区
    临界资源: 多个线程执行流共享的资源称为临界资源,它可能是一块内存、文件、网络连接等。对这些资源的并发访问可能导致不确定的结果或数据损坏。
    临界区: 是每个线程内部访问临界资源的代码段。在临界区内,对共享资源的访问需要进行互斥,以确保同一时刻只有一个线程能够进入临界区

  3. 互斥与原子性
    互斥: 互斥是一种机制,确保在任何时刻只有一个线程能够进入临界区,从而访问临界资源,通常对临界资源起保护作用。
    原子性: 表示一个操作是不可中断的,要么全部执行成功,要么完全不执行。在多线程环境中,原子操作是不会被其他线程中断的操作,确保原子性可以避免竞态条件和数据不一致性。

示例程序说明(不使用互斥量):
在这里插入图片描述
上述程序输出结果表明,由于多个线程同时访问和修改共享资源(available_tickets)而没有同步机制,导致竞态条件的发生。在输出中,票数递减,并最终变成负数。由于线程执行的顺序不确定,每次运行结果可能不同。

  1. 互斥量和加锁

在这里插入图片描述

  1. pthread_mutex_t 类型
    pthread_mutex_tPOSIX 线程库提供的互斥量类型,用于在多线程环境中同步对共享资源的访问。它是一个结构体类型,通常通过指针进行操作
  2. pthread_mutex_init 函数
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);

功能: 初始化互斥量对象。
参数
mutex 是指向要初始化的互斥量对象的指针。
attr 是指向互斥量属性对象的指针。可以为 NULL,表示使用默认属性。
返回值: 若成功,返回 0;否则,返回错误码

  1. pthread_mutex_destroy 函数
#include <pthread.h>int pthread_mutex_destroy(pthread_mutex_t *mutex); 

功能: 销毁互斥量对象。

参数: mutex 是指向要销毁的互斥量对象的指针。

返回值: 若成功,返回 0;否则,返回错误码。

  1. PTHREAD_MUTEX_INITIALIZER

PTHREAD_MUTEX_INITIALIZER 是一个宏,用于静态初始化一个互斥锁(mutex)。
在定义互斥量时,可以使用以下宏进行初始化:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

功能: 使用宏 PTHREAD_MUTEX_INITIALIZER 初始化互斥量对象。

注意: 此方法只能在定义互斥量时使用,不能在运行时再次初始化。

  1. pthread_mutex_lock 函数
 #include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);

功能pthread_mutex_lock 的功能是加锁,而且如果互斥量已经被锁定再次调用 pthread_mutex_lock 将会导致调用线程被阻塞,直到互斥量变为可用。

参数mutex 是指向要加锁的互斥量对象的指针。

返回值: 若成功,返回 0;否则,返回错误码。

  1. pthread_mutex_unlock 函数
 #include <pthread.h>int pthread_mutex_unlock(pthread_mutex_t *mutex);

功能: 解锁。释放互斥量,使其变为可用。

参数mutex 是指向要解锁的互斥量对象的指针。

返回值: 若成功,返回 0;否则,返回错误码。

示例程序说明(使用互斥量):
在这里插入图片描述

pthread_mutex_t mutex 主要用于保护临界区,对共享资源进行读取或修改的那部分代码。在多线程环境中,当多个线程同时访问共享资源而缺乏适当的同步机制时,可能导致数据不一致或产生竞争条件

通过调用 pthread_mutex_lockpthread_mutex_unlock
函数,程序可以执行互斥操作。在进入临界区前,线程通过加锁确保只有一个线程能够执行临界区代码。而在退出临界区后,通过解锁释放互斥锁,使其他线程有机会进入。

以上程序通过互斥量确保了多线程环境下对共享资源(票数)的安全访问,实现了线程安全的抢票操作。

3. 线程同步与竞态条件

在多线程编程中,多个线程共享进程的资源,这样就可能导致竞态条件的产生。竞态条件是由于多个线程对共享资源的访问顺序不确定而引起的问题。为了解决这一问题,引入了线程同步的概念。

程序示例(不使用线程同步)
在这里插入图片描述

从这个输出可以看出,线程2连续抢到了多张票而其他线程并没有执行抢票的机会。这是因为在没有使用线程同步的情况下,多个线程同时访问了共享资源,导致了竞态条件。

4. pthread库与条件变量

在多线程编程中,线程之间的协调与同步是至关重要的。pthread库提供了一系列工具,其中条件变量(pthread_cond_t)是一种强大的机制。

在这里插入图片描述

  1. pthread_cond_t类型
    pthread_cond_tpthread库中用于线程同步的一种机制,也被称为条件变量条件变量允许线程在等待某个条件满足时进入休眠状态,并在条件发生变化时被唤醒。条件变量通常与互斥锁一起使用,以确保多个线程对共享资源的安全访问。

  2. pthread_cond_init函数

#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);

功能:
pthread_cond_init 函数用于初始化条件变量,为其分配必要的资源。

参数:
cond:指向要初始化的条件变量的指针。
attr:条件变量的属性,通常设置为 NULL 表示使用默认属性。
返回值:
若成功初始化条件变量,返回 0;否则,返回错误码。

  1. pthread_cond_destroy 函数
#include <pthread.h>
int pthread_cond_destroy(pthread_cond_t *cond);

功能:
pthread_cond_destroy 函数用于销毁条件变量,释放相关资源。

参数:
cond:指向要销毁的条件变量的指针。
返回值:
若成功销毁条件变量,返回 0;否则,返回错误码。

  1. PTHREAD_MUTEX_INITIALIZER

PTHREAD_COND_INITIALIZER宏,是一种静态初始化pthread_cond_t类型的条件变量的方式。
在定义条件变量时,可以使用以下宏进行初始化:

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
  1. pthread_cond_wait函数
#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);

功能:
pthread_cond_wait 函数用于在等待条件变量的同时暂时释放互斥锁,使线程进入等待状态。

参数:
cond:指向要等待的条件变量的指针。
mutex:指向互斥锁的指针,用于保护对条件变量的访问。
返回值:
若成功,返回 0;否则,返回错误码。

  1. pthread_cond_broadcast函数
#include <pthread.h>
int pthread_cond_broadcast(pthread_cond_t *cond);

功能:
pthread_cond_broadcast 函数用于向所有等待在条件变量上的线程发送信号,使它们从等待状态中唤醒。

参数:
cond:指向要发送信号的条件变量的指针。
返回值:
若成功,返回 0;否则,返回错误码。

  1. pthread_cond_signal函数
#include <pthread.h>
int pthread_cond_signal(pthread_cond_t *cond);

功能
pthread_cond_signal 函数用于向等待在条件变量上的一个线程发送信号,使其从等待状态中唤醒。

参数
cond:指向要发送信号的条件变量的指针。
返回值
若成功,返回 0;否则,返回错误码。

程序示例(使用线程同步)
在这里插入图片描述

pthread_cond_init(&cond, NULL); 用于初始化条件变量 cond,与互斥锁一同使用,用于线程等待和唤醒。在线程执行函数中,通过 pthread_cond_wait(&cond, &mutex); 进行等待,释放互斥锁 mutex 进入阻塞状态。

主线程通过 pthread_cond_signal(&cond); 发送信号,唤醒一个等待条件变量的线程,被唤醒的线程在获取互斥锁之前不能继续执行。这确保了每个线程在唤醒后都能够争夺到互斥锁,避免了线程饥饿的情况。

5. 生产者-消费者

  1. 生产者消费者模型的概念

在这里插入图片描述

生产者消费者模型通过中间容器(阻塞队列)解决了生产者和消费者强耦合的问题。两者不直接通讯,而是通过阻塞队列传递数据。生产者产生数据后,无需等待消费者处理,直接放入阻塞队列。消费者不主动请求数据,而是直接从阻塞队列取出,阻塞队列充当了缓冲区的角色。

  1. 生产者、消费者之间的关系

在生产者消费者模型中存在三种关系:

生产者和生产者之间的互斥关系: 确保不同生产者之间的操作互斥,以保证数据的正确生成。

消费者和消费者之间的互斥关系: 确保不同消费者之间的操作互斥,防止并发访问导致问题。

生产者和消费者之间的互斥与同步关系: 实现互斥以保证读写安全,同时在缓冲区数据满或空时,确保能够互相等待和通知,实现同步操作。生产者线程和消费者线程是两种不同的角色,彼此之间通过缓冲区进行数据交流,这个缓冲区可被看作是一个特定结构的交换平台。

代码示例:

在这里插入图片描述

pthread_mutex_t mutex:互斥锁

作用: 保护对缓冲区的访问,确保在同一时刻只有一个线程能够访问或修改共享资源
使用场景: 在生产者和消费者访问缓冲区时,通过互斥锁进行加锁和解锁,避免多个线程同时修改共享资源导致数据不一致或冲突。


pthread_cond_t not_full:缓冲区不满的条件变量
作用: 用于通知生产者线程缓冲区不再是满的状态,可以继续生产数据。
使用场景: 当缓冲区满时,生产者线程通过等待该条件变量进入阻塞状态,直到消费者线程通知它缓冲区不再是满的。

pthread_cond_t not_empty:缓冲区不空的条件变量
作用: 用于通知消费者线程缓冲区不再是空的状态,可以继续消费数据。
使用场景: 当缓冲区为空时,消费者线程通过等待该条件变量进入阻塞状态,直到生产者线程通知它缓冲区不再是空的。

这个程序展示了基本的生产者-消费者模型,通过互斥锁和条件变量确保线程安全的访问共享资源。生产者不断生成随机数作为数据项并放入缓冲区,而消费者则从缓冲区中取出数据项进行消费。这种模型确保了在多线程环境中的安全数据传递和协同工作。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

EasyCaptcha,开源图形验证码新标杆!

引言&#xff1a; 随着互联网的普及&#xff0c;验证码已成为网站和应用程序中不可或缺的安全组件。它能够有效地防止自动化攻击、垃圾邮件和机器人活动。在众多验证码解决方案中&#xff0c;Easy-captcha以其简单易用和高度可定制的特点受到了开发者的青睐。本文将指导读者如…

leetcode:买卖股票最佳时机二

思路&#xff1a; 使用贪心算法&#xff1a;局部最优是将买卖过程中产生的正数进行相加&#xff0c;进而使得最后结果最大&#xff08;全局最优&#xff09;。 price [7,1,5,10,3,6,4] -6,4,5,-7,3,-2 正数相加就得到了最大 代码实现&#xff1a; 1.循环中下标从1开始 …

AI大模型开发架构设计(10)——AI大模型架构体系与典型应用场景

文章目录 AI大模型架构体系与典型应用场景1 AI大模型架构体系你了解多少?GPT 助手训练流程GPT 助手训练数据预处理2个训练案例分析 2 AI 大模型的典型应用场景以及应用架构剖析AI 大模型的典型应用场景AI 大模型应用架构 AI大模型架构体系与典型应用场景 1 AI大模型架构体系你…

蓝牙BLE学习-蓝牙广播

1.概念 什么叫做广播&#xff0c;顾名思义就像广场上的大喇叭一样&#xff0c;不停的向外传输着信号。不同的是&#xff0c;大喇叭传输的是音频信号&#xff0c;而蓝牙传输的是射频信号。 BLE使用的是无线电波传递信息&#xff0c;就是将数据编码&#xff0c;调制到射频信号中发…

【JavaEE】_CSS选择器

目录 1. 基本语法格式 2. 引入方式 2.1 内部样式 2.2 内联样式 2.3 外部样式 3. 基础选择器 3.1 标签选择器 3.2 类选择器 3.3 ID选择器 4. 复合选择器 4.1 后代选择器 4.2 子选择器 4.3 并集选择器 4.4 伪类选择器 1. 基本语法格式 选择器若干属性声明 2. 引入…

春节过半,预定的计划还没有开始

春节前就立下雄心勃勃的计划&#xff0c;想利春节假期开始搭一个人脸通WEB管理软件。但眼看春节过半&#xff0c;自己还没有开始动手呦。哎&#xff0c;突然紧张起来了。初二初三身体都不太舒服&#xff0c;不知道是怎么回事就感冒了&#xff0c;今晚更是高烧39.5&#xff0c;感…

python-分享篇-GUI界面开发-PyQt5-在窗口中弹出等待提示框

代码 # *_* coding : UTF-8 *_* # 文件名称 &#xff1a;waiting_prompt.py # 开发工具 &#xff1a;PyCharmfrom window import Ui_MainWindow # 导入窗体ui类 from PyQt5.QtWidgets import QMainWindow, QApplication # 导入qt窗体类 from PyQ…

【51单片机】LED点阵屏(江科大)

9.1LED点阵屏 1.LED点阵屏介绍 LED点阵屏由若干个独立的LED组成,LED以矩阵的形式排列,以灯珠亮灭来显示文字、图片、视频等。 2.LED点阵屏工作原理 LED点阵屏的结构类似于数码管,只不过是数码管把每一列的像素以“8”字型排列而已。原理图如下 每一行的阳极连在一起,每一列…

C++初阶:适合新手的手撕vector(模拟实现vector)

上次讲了常用的接口&#xff1a;C初阶&#xff1a;容器&#xff08;Containers&#xff09;vector常用接口详解 今天就来进行模拟实现啦 文章目录 1.基本结构与文件规划2.空参构造函数&#xff08;constructor)4.基本函数&#xff08;size(),capacity(),resize(),reserve())4.增…

AcWing 802. 区间和 离散化

文章目录 题目链接题目描述解题思路代码实现总结 题目链接 链接: AcWing 802. 区间和 题目描述 解题思路 离散化是一种常用的技巧&#xff0c;它能够将原始的连续数值转换为一组离散的值&#xff0c;从而简化问题的处理。在这段代码中&#xff0c;离散化的过程主要分为三个步…

STM32自学☞定时器定时中断案例

timer_interrupt.c文件 /* 初始化函数编写步骤&#xff1a; 1.打开时钟 2.选择时基单元的时钟源&#xff08;内部时钟源&#xff09; 3.配置时基单元 4.NVIC配置 5.启动定时器 */ #include "stm32f10x.h" #include "stm32f10x_tim.h" #include …

探索Nginx:强大的开源Web服务器与反向代理

一、引言 随着互联网的飞速发展&#xff0c;Web服务器在现代技术架构中扮演着至关重要的角色。Nginx&#xff08;发音为“engine x”&#xff09;是一个高性能的HTTP和反向代理服务器&#xff0c;也是一个IMAP/POP3/SMTP代理服务器。Nginx因其卓越的性能、稳定性和灵活性&…