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

Linux应用:线程进阶

线程同步之信号量

信号量(Semaphore)是一个整型的计数器,用于控制对共享资源的访问。它通过 PV 操作来实现同步,P 操作将信号量的值减 1,如果值小于 0 则线程阻塞;V 操作将信号量的值加 1,如果有线程在等待则唤醒一个等待的线程。

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

sem_t sem;  // 定义信号量

void* thread_function(void* arg) {
    // P操作
    sem_wait(&sem);
    printf("子线程进入临界区\n");
    // 模拟临界区操作
    sleep(2);
    printf("子线程离开临界区\n");
    // V操作
    sem_post(&sem);
    return NULL;
}

int main() {
    pthread_t thread;
    // 初始化信号量,初始值设为1
    sem_init(&sem, 0, 1);

    // 创建线程
    if (pthread_create(&thread, NULL, thread_function, NULL) != 0) {
        perror("线程创建失败");
        return 1;
    }

    // 主线程等待一段时间
    sleep(1);
    // P操作 作用是将信号量的值减 1  值大于0才能减
    sem_wait(&sem);
    printf("主线程进入临界区\n");
    // 模拟临界区操作
    sleep(2);
    printf("主线程离开临界区\n");
    // V操作 使信号量的值加 1
    sem_post(&sem);

    // 等待线程结束
    if (pthread_join(thread, NULL) != 0) {
        perror("线程等待失败");
        return 1;
    }

    // 销毁信号量
    sem_destroy(&sem);
    return 0;
}

sem_t属于信号量类型,sem是所定义的信号量变量。

sem_wait(&sem):这是信号量的 P 操作,其作用是将信号量的值减 1。若信号量的值为 0,线程会被阻塞,直至信号量的值大于 0。
printf(“线程进入临界区\n”);:输出线程进入临界区的信息。
sleep(2);:模拟在临界区内进行的操作,这里让线程休眠 2 秒。
printf(“线程离开临界区\n”);:输出线程离开临界区的信息。
sem_post(&sem):这是信号量的 V 操作,会使信号量的值加 1,同时唤醒可能处于阻塞状态的线程。

pthread_t thread;:定义一个线程标识符。
sem_init(&sem, 0, 1);:对信号量进行初始化,第二个参数为 0 表示该信号量仅在线程间共享,第三个参数 1 为信号量的初始值。
pthread_create(&thread, NULL, thread_function, NULL):创建一个新线程,新线程会执行thread_function函数。
sleep(1);:主线程等待 1 秒,确保新线程有机会运行。
sem_wait(&sem);:主线程执行 P 操作,尝试进入临界区。
printf(“主线程进入临界区\n”);:输出主线程进入临界区的信息。
sleep(2);:模拟主线程在临界区内的操作。
printf(“主线程离开临界区\n”);:输出主线程离开临界区的信息。
sem_post(&sem);:主线程执行 V 操作,离开临界区。
pthread_join(thread, NULL);:主线程等待新线程结束。
sem_destroy(&sem);:销毁信号量,释放相关资源。

在这里插入图片描述
创建了一个新线程,利用信号量实现了主线程和新线程对临界区的互斥访问。信号量的初始值为 1,同一时刻仅允许一个线程进入临界区。通过 P 操作和 V 操作保证了线程安全,防止多个线程同时访问临界区从而引发数据竞争问题。

线程同步之互斥锁

互斥锁(Mutex)是一种特殊的二元信号量(初始值为 1),用于保证在同一时刻只有一个线程能够访问共享资源,也就是一次只有一个线程能够获得锁,其他线程必须等待锁被释放。

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

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;  // 定义并初始化互斥锁

void* thread_function(void* arg) {
    // 加锁
    pthread_mutex_lock(&mutex);
    printf("线程进入临界区\n");
    // 模拟临界区操作
    sleep(2);
    printf("线程离开临界区\n");
    // 解锁
    pthread_mutex_unlock(&mutex);
    return NULL;
}

int main() {
    pthread_t thread;

    // 创建线程
    if (pthread_create(&thread, NULL, thread_function, NULL) != 0) {
        perror("线程创建失败");
        return 1;
    }

    // 主线程等待一段时间
    sleep(1);
    // 加锁
    pthread_mutex_lock(&mutex);
    printf("主线程进入临界区\n");
    // 模拟临界区操作
    sleep(2);
    printf("主线程离开临界区\n");
    // 解锁
    pthread_mutex_unlock(&mutex);

    // 等待线程结束
    if (pthread_join(thread, NULL) != 0) {
        perror("线程等待失败");
        return 1;
    }

    return 0;
}

在这里插入图片描述
stdio.h:标准输入输出库,提供了像 printf 这样用于输出信息到控制台的函数。
pthread.h:多线程相关的库,包含了创建线程、管理线程和使用互斥锁等操作所需的函数和数据类型。
unistd.h:提供了 sleep 函数,用于让线程暂停执行一段时间。

pthread_mutex_t 是一种数据类型,用于表示互斥锁。
mutex 是定义的互斥锁变量。
PTHREAD_MUTEX_INITIALIZER 是一个宏,用于静态初始化互斥锁,将其设置为初始状态。

pthread_mutex_lock(&mutex):调用该函数尝试获取互斥锁 mutex。如果互斥锁当前未被其他线程持有,调用线程会获取到锁并继续执行后续代码;如果互斥锁已经被其他线程持有,调用线程会被阻塞,直到锁被释放。
printf(“线程进入临界区\n”);:输出信息表明线程已经成功进入临界区。
sleep(2);:模拟线程在临界区内执行一些耗时操作,这里让线程暂停 2 秒。
printf(“线程离开临界区\n”);:输出信息表明线程即将离开临界区。
pthread_mutex_unlock(&mutex):调用该函数释放互斥锁,使得其他等待该锁的线程有机会获取锁并进入临界区。

pthread_t thread;:定义一个 pthread_t 类型的变量 thread,用于存储新创建线程的标识符。
pthread_create(&thread, NULL, thread_function, NULL):创建一个新线程,新线程将执行 thread_function 函数。如果线程创建失败,pthread_create 函数会返回非零值,此时程序会输出错误信息并终止。
sleep(1);:主线程暂停 1 秒,目的是让新创建的线程有机会先运行并尝试获取互斥锁。
pthread_mutex_lock(&mutex):主线程尝试获取互斥锁。如果此时新线程已经持有锁,主线程会被阻塞。
printf(“主线程进入临界区\n”);:输出信息表明主线程已经成功进入临界区。
sleep(2);:模拟主线程在临界区内执行一些耗时操作,暂停 2 秒。
printf(“主线程离开临界区\n”);:输出信息表明主线程即将离开临界区。
pthread_mutex_unlock(&mutex):主线程释放互斥锁。
pthread_join(thread, NULL):主线程等待新创建的线程执行完毕。如果等待过程中出现错误,pthread_join 函数会返回非零值,程序会输出错误信息并终止。

通过创建一个新线程和主线程,利用互斥锁实现了对临界区的互斥访问。互斥锁保证了同一时间只有一个线程能够进入临界区执行操作,避免了多个线程同时访问共享资源可能导致的数据不一致问题。当一个线程进入临界区时,会先获取互斥锁,操作完成后释放锁,使得其他线程可以继续竞争进入临界区。

线程同步之条件变量

条件变量(Condition Variable)用于线程间的同步,它允许一个线程等待某个条件满足后再继续执行。通常与互斥锁一起使用,线程在获取互斥锁后检查条件是否满足,如果不满足则通过条件变量等待,并释放互斥锁;当另一个线程改变了条件后,通过条件变量唤醒等待的线程,等待的线程重新获取互斥锁后继续执行。

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

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int flag = 0;

void* thread_function(void* arg) {
    // 加锁
    pthread_mutex_lock(&mutex);
    while (flag == 0) {
        // 等待条件变量,同时释放互斥锁
        printf("线程得到锁\n");
        sleep(1);
        printf("线程释放锁 1 \n");
        pthread_cond_wait(&cond, &mutex);
        printf("线程释放锁 2 \n");
    }
    printf("线程被唤醒,条件满足\n");
    // 解锁
    pthread_mutex_unlock(&mutex);
    return NULL;
}

int main() {
    pthread_t thread;

    // 创建线程
    if (pthread_create(&thread, NULL, thread_function, NULL) != 0) {
        perror("线程创建失败");
        return 1;
    }

    // 主线程等待一段时间
    sleep(2);
    // 加锁
    pthread_mutex_lock(&mutex);
    flag = 1;
    // 唤醒等待的线程
    printf("唤醒等待的线程 1\n");
    pthread_cond_signal(&cond);
    printf("唤醒等待的线程 2\n");
    // 解锁
    pthread_mutex_unlock(&mutex);
    printf("唤醒等待的线程 3\n");

    // 等待线程结束
    if (pthread_join(thread, NULL) != 0) {
        perror("线程等待失败");
        return 1;
    }

    return 0;
}

在这里插入图片描述
mutex:互斥锁,用于保护共享资源,PTHREAD_MUTEX_INITIALIZER 是一个宏,用于静态初始化互斥锁。
cond:条件变量,用于线程间的同步通信,PTHREAD_COND_INITIALIZER 是一个宏,用于静态初始化条件变量。
flag:共享变量,作为线程间通信的标志,初始值为 0。

pthread_mutex_lock(&mutex):线程进入临界区前加锁,确保同一时间只有一个线程可以访问共享资源。
while (flag == 0):检查共享变量 flag 的值,如果为 0,则线程需要等待条件满足。
pthread_cond_wait(&cond, &mutex):这是一个关键函数,它会做两件事:
释放当前持有的互斥锁 mutex,允许其他线程进入临界区。
阻塞当前线程,直到条件变量 cond 被其他线程发出信号(pthread_cond_signal 或 pthread_cond_broadcast)。
当线程被唤醒后,它会重新获取互斥锁 mutex,继续执行后续代码。
printf(“线程被唤醒,条件满足\n”);:当 flag 变为 1 时,线程被唤醒,输出相应信息。
pthread_mutex_unlock(&mutex):线程离开临界区,释放互斥锁。

pthread_create(&thread, NULL, thread_function, NULL):创建一个新线程,执行 thread_function 函数。
sleep(2):主线程休眠 2 秒,确保新线程有足够的时间进入等待状态。
pthread_mutex_lock(&mutex):主线程进入临界区,加锁。
flag = 1:修改共享变量 flag 的值,表示条件已经满足。
pthread_cond_signal(&cond):发送信号给等待在条件变量 cond 上的线程,唤醒其中一个线程。
pthread_mutex_unlock(&mutex):主线程离开临界区,释放互斥锁。
pthread_join(thread, NULL):主线程等待新线程执行完毕,确保程序正常结束。

通过互斥锁和条件变量实现了线程间的同步。新线程在 flag 为 0 时等待条件满足,主线程在适当的时候修改 flag 的值并发送信号唤醒等待的线程。这样可以避免线程忙等待,提高程序的效率。

相关文章:

  • hackmyvm-reversteg
  • Modbus TCP返回报文
  • 简单介绍一下Unity中的ScriptableObject
  • Skynet 框架中 gateserver、gate、watchdog 的关系
  • browser-use 库网页元素点击测试工具
  • 多路转接epoll
  • 基于杜鹃鸟鲶鱼优化(Cuckoo Catfish Optimizer,CCO)算法的多个无人机协同路径规划(可以自定义无人机数量及起始点),MATLAB代码
  • 输入百分比校验(数字非负数保留2位不四舍五入)
  • ABAQUS圆柱体纤维重力堆积3D模型
  • 关于bug总结记录
  • 软件测试之fiddler详解
  • 计算机二级(C语言)考试高频考点总汇(二)—— 控制流、函数、数组和指针
  • 破解AI焦虑,YonSuite给出了一份企业AI落地路线图
  • 学习日记0327
  • 基于FPGA的ESP8266无线数据传输(温湿度DTH11、光照强度BH1750、WIFI模块)连接中国移动onenet云平台,仿真+上板
  • 数据大屏点亮工业互联网的智慧之眼
  • 材料科学基础:空间群与点群(2)
  • Redis6为什么引入了多线程?
  • TDengine 中的保留关键词
  • 利用新一代雷达传感器增强ADAS系统的检测和计算(TI文档)
  • 快速建设网站外链/大数据查询官网
  • 中国做网站最好的公司/下载百度地图2022最新版官方
  • 建设委员会网站首页/seo实战密码第三版pdf
  • 做奶茶吧店网站/百度推广运营专员
  • 专业的响应式网站建设/网站制作软件
  • 网站广审怎么做/seo搜索优化专员招聘