【Linux】使用信号量semaphore控制多个线程按序交替打印

本文介绍了如何利用信号量semaphore解决多线程同步问题,特别是针对多线程按特定顺序交替打印的场景。通过详细分析问题并提供示例代码,展示了信号量在控制线程执行顺序中的应用,最后给出了运行结果。

【1】题目描述

在这里插入图片描述

【2】问题分析

我们可以使用信号量进行同步三个线程固定打印顺序如图所示:

在这里插入图片描述

【3】示例代码

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

sem_t sem1;
sem_t sem2;
sem_t sem3;

void *pthread_fun1(void*arg)	//线程函数1打印a
{
   
   
    int i=0;
    for(;i<10;++i)
    {
   
   
        sem_wait(&sem1);	
<think> 我们有两个线程线程1打印A、C、E,线程2打印B、D。需要交替执行,即顺序为:A, B, C, D, E。 注意:总共5个字母,线程1打印3个(A、C、E),线程2打印2个(B、D)。因此,我们需要确保线程1先打印,然后线程2,再线程1,线程2,最后线程1。 我们可以使用两个信号量来实现同步: - 信号量sem1:初始化为1,控制线程1的打印。 - 信号量sem2:初始化为0,控制线程2的打印。 逻辑如下: 1. 线程1先执行,因为sem1初始为1,线程1获取sem1后打印A,然后释放sem2(使得sem2变为1,线程2可运行)。 2. 线程2获取sem2(此时为1)后打印B,然后释放sem1(使得sem1变为1,线程1可运行)。 3. 线程1继续打印C,然后释放sem2。 4. 线程2打印D,然后释放sem1。 5. 线程1打印E,然后释放sem2(但此时线程2已经不需要再运行了,因为线程2只打印两个字母)。 但是,注意线程2在打印完两个字母后就应该退出,线程1打印完三个字母后退出。 因此,我们需要在两个线程的循环中控制打印次数。我们可以通过传递参数或设置全局变量来控制每个线程打印次数。 具体步骤: - 全局定义两个信号量:sem1和sem2。 - 线程1的任务: for (int i = 0; i < 3; i++) { sem_wait(&sem1); // 等待信号量sem1 打印字母(A、C、E) sem_post(&sem2); // 释放信号量sem2 } - 线程2的任务: for (int i = 0; i < 2; i++) { sem_wait(&sem2); // 等待信号量sem2 打印字母(B、D) sem_post(&sem1); // 释放信号量sem1 } 注意:初始化时,sem1为1,sem2为0,这样线程1先执行,线程2等待。 另外,我们需要包含必要的头文件,并检查信号量线程函数的返回(这里为了简洁,省略了部分错误处理,实际应用中建议添加)。 程序结束前,要销毁信号量,并等待两个线程结束。 由于线程执行顺序不确定,我们需要确保主线程等待两个子线程结束。在这个例子中,我们创建两个线程,然后分别等待它们结束。 但是,这里我们只创建了两个线程,所以主线程需要调用pthread_join两次。 注意:线程函数中打印字母时,需要知道当前应该打印哪个字母。我们可以用一个数组来存储线程1要打印的字母:{'A','C','E'},线程2:{'B','D'},然后通过循环索引来打印。 代码实现: 步骤: 1. 定义信号量sem1和sem2。 2. 初始化信号量:sem1初始化为1,sem2初始化为0。 3. 创建两个线程,分别执行线程1和线程2的函数。 4. 等待两个线程结束。 5. 销毁信号量。 注意:线程函数中,循环次数分别为3和2。 下面给出完整的代码: </think> 以下是使用C语言信号量实现两个线程交替打印字母的完整程序。线程1打印A、C、E,线程2打印B、D,确保交替执行: ```c #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <semaphore.h> // 定义信号量和共享资源 sem_t sem_thread1; sem_t sem_thread2; // 线程1函数:打印A、C、E void* thread1_func(void* arg) { char letters[] = {'A', 'C', 'E'}; // 线程1的打印序列 for (int i = 0; i < 3; i++) { sem_wait(&sem_thread1); // 等待信号量1 printf("Thread 1: %c\n", letters[i]); // 打印字母 fflush(stdout); // 立即刷新输出缓冲区 sem_post(&sem_thread2); // 释放信号量2 } return NULL; } // 线程2函数:打印B、D void* thread2_func(void* arg) { char letters[] = {'B', 'D'}; // 线程2的打印序列 for (int i = 0; i < 2; i++) { sem_wait(&sem_thread2); // 等待信号量2 printf("Thread 2: %c\n", letters[i]); // 打印字母 fflush(stdout); // 立即刷新输出缓冲区 sem_post(&sem_thread1); // 释放信号量1 } return NULL; } int main() { pthread_t tid1, tid2; // 初始化信号量 sem_init(&sem_thread1, 0, 1); // 初始1,让线程1先执行 sem_init(&sem_thread2, 0, 0); // 初始0,线程2等待 // 创建线程 pthread_create(&tid1, NULL, thread1_func, NULL); pthread_create(&tid2, NULL, thread2_func, NULL); // 等待线程结束 pthread_join(tid1, NULL); pthread_join(tid2, NULL); // 销毁信号量 sem_destroy(&sem_thread1); sem_destroy(&sem_thread2); return 0; } ``` ### 程序说明及关键点 1. **信号量初始化**: - `sem_thread1` 初始=1(线程1先执行) - `sem_thread2` 初始=0(线程2等待) ```c sem_init(&sem_thread1, 0, 1); // 线程1信号量 sem_init(&sem_thread2, 0, 0); // 线程2信号量 ``` 2. **同步逻辑**: - 线程1打印后触发线程2:`sem_post(&sem_thread2)` - 线程2打印后触发线程1:`sem_post(&sem_thread1)` ```c // 线程1核心逻辑 sem_wait(&sem_thread1); // 等待许可 printf("Thread 1: %c\n", letters[i]); sem_post(&sem_thread2); // 释放线程2许可 // 线程2核心逻辑 sem_wait(&sem_thread2); // 等待许可 printf("Thread 2: %c\n", letters[i]); sem_post(&sem_thread1); // 释放线程1许可 ``` 3. **执行流程**: ``` Thread1获得信号 → 打印A → 释放Thread2信号 Thread2获得信号 → 打印B → 释放Thread1信号 Thread1获得信号 → 打印C → 释放Thread2信号 Thread2获得信号 → 打印D → 释放Thread1信号 Thread1获得信号 → 打印E → 结束 ``` 4. **输出结果**: ``` Thread 1: A Thread 2: B Thread 1: C Thread 2: D Thread 1: E ``` ### 编译运行方法 ```bash gcc -o thread_print thread_print.c -lpthread ./thread_print ``` ### 关键注意事项 1. **信号量初始化顺序**:必须确保线程1的信号量初始大于线程2 2. **fflush(stdout)**:强制刷新输出缓冲区,避免打印顺序错乱 3. **线程安全**:使用信号量替代互斥锁实现精确时序控制 4. **资源释放**:程序结束时销毁信号量防止资源泄漏 此实现通过两个信号量形成"许可传递链",严格保证了打印顺序为A→B→C→D→E[^1][^2]。信号量初始的设置和post/wait的配对使用交替执行的关键[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值