【c】linux下互斥锁的使用

本文通过实例展示了在多线程环境下,不使用和使用mutex进行文件打印操作的区别,解释了mutex如何确保线程安全。使用mutex的程序避免了输出混乱,实现了线程间的同步。同时,文章探讨了mutex的工作原理,包括线程的阻塞与自旋锁的概念,强调了线程调度对执行顺序的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


提示:以下是本篇文章正文内容,下面案例可供参考

一、mutex使用

不使用mutex的多线程打印

#include "stdio.h"
#include "pthread.h"
#define FILENAME "hello.txt"

typedef struct {
    FILE *f;
    int id;
} f_and_id;


void *doit(void *data){
    f_and_id data_val = *(f_and_id *)data;
    printf("...%d\n", data_val.id);
    int start = data_val.id;
    int end = start+1;
    FILE *fp = data_val.f;
    setbuf(fp, NULL); // 设置无缓冲
    for(int i = (start*10);i<(end*10);i++){
        fprintf(fp, "%d\t", i);
    }
    fprintf(fp, "\n");
    return NULL;
}

int main(){
    int num_thread=5;
    pthread_t ths[5];
    FILE *fp = fopen(FILENAME, "a+");
    f_and_id fids[num_thread];
    for(int i=0;i<num_thread;i++){
        f_and_id f = {.f=fp, .id=i};
        fids[i] = f;
    }
    for(int i=0;i<num_thread;i++){
        if(pthread_create(&ths[i], NULL, doit, (void *)&fids[i])!=0){
            printf("thread create failed\n");
            return 1;
        }
    }
    for (int i=0; i<num_thread;i++){
        pthread_join(ths[i], NULL);
    }
    fclose(fp);
}

输出结果:
请添加图片描述
可以看出线程间进行了切换,五个线程都使用了同一个file*,导致输入到文件的内容产生混乱。


使用mutex的多线程打印

#include "stdio.h"
#include "pthread.h"
#define FILENAME "hello.txt"

pthread_mutex_t mutex_lock;

typedef struct {
    FILE *f;
    int id;
} f_and_id;


void *doit(void *data){
    if(pthread_mutex_lock(&mutex_lock)!=0){
        fprintf(stdout, "lock error!\n");
    }
    f_and_id data_val = *(f_and_id *)data;
    printf("...%d\n", data_val.id);
    int start = data_val.id;
    int end = start+1;
    FILE *fp = data_val.f;
    setbuf(fp, NULL); // 设置无缓冲
    for(int i = (start*10);i<(end*10);i++){
        fprintf(fp, "%d\t", i);
    }
    fprintf(fp, "\n");
    pthread_mutex_unlock(&mutex_lock);
    return NULL;
}

int main(){
    int num_thread=5;
    pthread_t ths[5];
    FILE *fp = fopen(FILENAME, "a+");
    f_and_id fids[num_thread];
    for(int i=0;i<num_thread;i++){
        f_and_id f = {.f=fp, .id=i};
        fids[i] = f;
    }
    for(int i=0;i<num_thread;i++){
        if(pthread_create(&ths[i], NULL, doit, (void *)&fids[i])!=0){
            printf("thread create failed\n");
            return 1;
        }
    }
    for (int i=0; i<num_thread;i++){
        pthread_join(ths[i], NULL);
    }
    fclose(fp);
    pthread_mutex_destroy(&mutex_lock);
}

输出结果:
请添加图片描述
线程获得锁之后执行完自己的十次打印后才释放锁,保证了自己这一行的打印完毕。
但是为什么五个线程执行的顺序是从0到4(我试了好多次顺序都一致)现在还不能理解,
这有关CPU的线程调度。

二、原理

mutex的执行方式

当先执行的线程A获得了mutex,其后执行的线程B发现mutex已经被占有,会进入阻塞状态,把cpu让给其他线程,当锁被释放时,线程B重新进入就绪状态等待被cpu选中执行。可以看到其中有两次线程的切换,而切换线程上下文是需要时间的,大概在几十纳秒到几微秒之间。

自旋锁

自旋锁在先执行线程A获得锁之后,后执行的线程B会一直占用cpu(类似while死循环),等待锁被释放。这样可以省去线程切换带来的开销,但是对于单核的机器来说,如果没有抢占式的调度器(通过时钟中断进程),自旋锁完全不能使用,因为这个线程会一直占用这个核心。而对于多核而言,可以在其他核心上等待锁释放。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值