提示:以下是本篇文章正文内容,下面案例可供参考
一、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死循环),等待锁被释放。这样可以省去线程切换带来的开销,但是对于单核的机器来说,如果没有抢占式的调度器(通过时钟中断进程),自旋锁完全不能使用,因为这个线程会一直占用这个核心。而对于多核而言,可以在其他核心上等待锁释放。