Linux系统编程55 线程 - 互斥锁分析

引入互斥量:

线程间 竞争故障:

实验1:线程间竞争故障,用sleep()放大竞争故障

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>


#define THRNUM 20
#define FNAME "/home/mhr/Desktop/xitongbiancheng/parallel/thread/posix/out"
#define LINESIZE 1024 //提前写好1

static void *thr_add(void *p)
{
	FILE *fp;
	char linebuf[LINESIZE];

	fp = fopen(FNAME,"r+");
	if(fp == NULL)
	{
		perror("fopen()");
		exit(1);
	}

	fgets(linebuf,LINESIZE,fp);
	fseek(fp,0,SEEK_SET);

	sleep(1);	
	
	fprintf(fp,"%d\n",atoi(linebuf)+1);
	fclose(fp);
	pthread_exit(NULL);
} 

int main()
{
	pthread_t tid[THRNUM];
	int i,err;

	for(i = 0; i < THRNUM; i++)
	{
		err = pthread_create(tid+i,NULL,thr_add,NULL);
		if(err)
		{
			fprintf(stderr,"pthread_create(): %s\n",strerror(err));
			exit(1);
		}
	}

	for(i = 0;i < THRNUM; i++)
	{
		pthread_join(tid[i],NULL);
	}

	exit(0);
	
}

由于是虚拟机 单核,所以模拟不出来多核的效果,故 在thr_add() 中 sleep(1) 一秒,比作调度器调度其他线程,放大各个线程之间的竞争,看结果 发现,创建出来的20个线程,全部都拿到了 FNAME文件,并拿到的值都是1,sleep()后 开始加一写值,导致重复写了20次2值到文件中。

出现这个问题的原因就是 线程之间的竞争导致的。


互斥量:

在这里插入图片描述

划红线的这句话太重要了!!! 对互斥量进行加锁以后,任何其他试图再次对互斥量加锁的线程都会被阻塞,直至当前线程释放该互斥锁
就算是自己在没有解锁的情况下,想再次持锁,也会被阻塞!!!

NAME
       pthread_mutex_destroy, pthread_mutex_init — destroy and initialize a mutex,初始化和销毁 一个 互斥量

SYNOPSIS
       #include <pthread.h>


SYNOPSIS
       #include <pthread.h>
       
	 //销毁
       int pthread_mutex_destroy(pthread_mutex_t *mutex);

//动态初始化
       int pthread_mutex_init(pthread_mutex_t *restrict mutex,
           const pthread_mutexattr_t *restrict attr);

//静态初始化方式
       pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

对互斥量上锁,解锁:

在这里插入图片描述

NAME
pthread_mutex_lock, pthread_mutex_trylock, pthread_mutex_unlock — lock and unlock a mutex

扫描二维码关注公众号,回复: 12940100 查看本文章

SYNOPSIS
#include <pthread.h>

	//以阻塞的方式 限制住某段代码的执行
   int pthread_mutex_lock(pthread_mutex_t *mutex);
   
   //以非阻塞的方式 限制住某段代码的执行
   int pthread_mutex_trylock(pthread_mutex_t *mutex);

	//解锁
   int pthread_mutex_unlock(pthread_mutex_t *mutex); 

误区:
不要直接理解为 只要有 pthread_mutex_lock()语句,后面的代码都是临界区,这样容易犯懵。而是要理解为:只我要拿到了锁,那么当其他线程要这个锁的时候,就拿不到锁,就会阻塞。利用这个规则,制造我们需要的临界区。

实验2:修改 上面实验,保证 同一个时间,只能一个人做的代码 为临界区。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>


#define THRNUM 20
#define FNAME "/home/mhr/Desktop/xitongbiancheng/parallel/thread/posix/out"
#define LINESIZE 1024

static pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;

static void *thr_add(void *p)
{
	FILE *fp;
	char linebuf[LINESIZE];

	fp = fopen(FNAME,"r+");
	if(fp == NULL)
	{
		perror("fopen()");
		exit(1);
	}

pthread_mutex_lock(&mut); //上锁

	fgets(linebuf,LINESIZE,fp);
	fseek(fp,0,SEEK_SET);

	//sleep(1);	
	
	fprintf(fp,"%d\n",atoi(linebuf)+1); // 全缓冲,需要fflush 或者 fclose 刷新 才能写到目标文件
	fclose(fp);// fprintf(fp  全缓冲,需要fflush 或者 fclose 刷新 才能写到目标文件,所以也在临界区

pthread_mutex_unlock(&mut); //解锁

	pthread_exit(NULL);
} 

int main()
{
	pthread_t tid[THRNUM];
	int i,err;

	for(i = 0; i < THRNUM; i++)
	{
		err = pthread_create(tid+i,NULL,thr_add,NULL);
		if(err)
		{
			fprintf(stderr,"pthread_create(): %s\n",strerror(err));
			exit(1);
		}
	}

	for(i = 0;i < THRNUM; i++)
	{
		pthread_join(tid[i],NULL);
	}

	pthread_mutex_destroy(&mut);
	exit(0);
	
}




mhr@ubuntu:~/Desktop/xitongbiancheng/parallel/thread/posix$ 
mhr@ubuntu:~/Desktop/xitongbiancheng/parallel/thread/posix$ gcc add.c -lpthread
mhr@ubuntu:~/Desktop/xitongbiancheng/parallel/thread/posix$ cat out 
1
mhr@ubuntu:~/Desktop/xitongbiancheng/parallel/thread/posix$ 
mhr@ubuntu:~/Desktop/xitongbiancheng/parallel/thread/posix$ 
mhr@ubuntu:~/Desktop/xitongbiancheng/parallel/thread/posix$ ./a.out 
mhr@ubuntu:~/Desktop/xitongbiancheng/parallel/thread/posix$ cat out 
21
mhr@ubuntu:~/Desktop/xitongbiancheng/parallel/thread/posix$ ./a.out 
mhr@ubuntu:~/Desktop/xitongbiancheng/parallel/thread/posix$ cat out 
41
mhr@ubuntu:~/Desktop/xitongbiancheng/parallel/thread/posix$ ./a.out 
mhr@ubuntu:~/Desktop/xitongbiancheng/parallel/thread/posix$ cat out 
61
mhr@ubuntu:~/Desktop/xitongbiancheng/parallel/thread/posix$ 

实验 3 ,互斥量,加深理解:规定时间内 四个线程 按顺序 分别不停的向终端 输出 a,b,c,d 每个线程输出一个字母

创建四个互斥锁,四个线程,临界区代码中,每个线程负责持锁,输出,并释放下一个线程的锁。这样 每个线程就可以轮询持锁输出。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <unistd.h>

#define THRNUM 4

static pthread_mutex_t mut[THRNUM];

static int next(int n)
{
	if(n+1 == THRNUM)
		return 0;
	return n+1;

}

static void *thr_add(void *p)
{
	int n = (int)p;
	int c = 'a' + (int)p;
	while(1)
	{
		pthread_mutex_lock(mut+n);
		write(1,&c,1);
		pthread_mutex_unlock(mut+next(n));//这里会执行 被阻塞的目标线程,当前线程由于没有释放自己持有的锁,会被再次阻塞。
	}
		

	pthread_exit(NULL);
} 

int main()
{
	pthread_t tid[THRNUM];
	int i,err;

	for(i = 0; i < THRNUM; i++)
	{
	
		pthread_mutex_init(mut+i,NULL);
		pthread_mutex_lock(mut+i);

		err = pthread_create(tid+i,NULL,thr_add,(void *)i);
		if(err)
		{
			fprintf(stderr,"pthread_create(): %s\n",strerror(err));
			exit(1);
		}
	}

	pthread_mutex_unlock(mut+0);//释放线程1的锁

	alarm(5);

	for(i = 0;i < THRNUM; i++)
	{
		pthread_join(tid[i],NULL);
	}

	exit(0);
	
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/LinuxArmbiggod/article/details/114264585