linux线程相关知识学习笔记2——线程同步之信号量

前言:通过一个任务导入并分析线程同步的信号量。

 

1、任务:用户从终端输入任意字符,然后统计字符个数并显示,输入end则结束。

 

2、分析:

使用多线程实现:主线程获取用户输入并判断是否退出,子线程计数。

 

3、信号量的使用及编程实现

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

char buf[200] = {0};
sem_t sem;	//定义一个信号量

//子线程程序,作用是统计buf中的字符个数并打印
void * func(void * arg)
{
	//子线程首先应该有个循环
	//循环中阻塞在等待主线程激活的时候,子线程被激活后就去获取buf中的字符长度,
	//然后打印;
	//完成打印后,再次被阻塞
	sem_wait(&sem);  //阻塞
	while(strncmp(buf, "end", 3) != 0)
	{
		printf("本次输入了%d个字符\n", (int)strlen(buf));
		memset(buf, 0, sizeof(buf));
		sem_wait(&sem);
	}
	
	pthread_exit(NULL);  //子线程退出
}


int main(void)
{
	int ret = -1;
	pthread_t th = -1;
	
	sem_init(&sem, 0, 0);	//初始化信号量
	
	ret = pthread_create(&th, NULL, func, NULL);	//创建子线程
	if (ret != 0)
	{
		printf("pthread_create error.\n");
		exit(-1);
	}
	
	printf("输入一个字符串,以回车结束\n");
	
	while(scanf("%s", buf))
	{
		//去比较用户输入的是不是end,如果是则退出,如果不是则继续
		if (!strncmp(buf, "end", 3))
		{
			sem_post(&sem);	//抛出信号量
			printf("程序结束\n");
			//exit(0);
			break;
		}
		
		//主线程在收到用户输入的字符串,并且确认不是end后
		//就去发信号激活子线程来计数。
		//子线程被阻塞,主线程可以激活,这就是线程的同步问题。
		//信号量就可以用来实现这个线程同步
		sem_post(&sem);	//抛出信号量
	}
	
	printf("等待回收子线程\n");
	ret = pthread_join(th, NULL);	//回收子线程
	if (ret != 0)
	{
		printf("pthread_join error.\n");
		exit(-1);
	}
	printf("子线程回收成功\n");
	
	sem_destroy(&sem);	//销毁信号量
	
	return 0;
}

 

 

4、线程同步之互斥锁

4.1、什么是互斥锁

(1)互斥锁又叫互斥量(mutex)

(2)相关函数:

  • pthread_mutex_init
  • pthread_mutex_destroy
  • pthread_mutex_lock
  • pthread_mutex_unlock

(3)互斥锁和信号量的关系:可以认为互斥锁是一种特殊的信号量

(4)互斥锁主要用来实现关键代码段保护

 

4.2、用互斥锁来实现关键段代码保护

注意:在编写程序,查询pthread_mutex_init 先关函数时,如:

man 3 pthread_mutex_init 时提示找不到函数,说明你当前linux系统环境没有安装pthread相关的man手册。

安装方法:

(1)虚拟机能上网;

(2)使用安装命令: sudo apt-get install manpages-posix-dev

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

char buf[200] = {0};
pthread_mutex_t mutex;	//定义一个信号量
unsigned char flag = 0;     //标志位

//子线程程序,作用是统计buf中的字符个数并打印
void * func(void * arg)
{
	//子线程首先应该有个循环
	//循环中阻塞在等待主线程激活的时候,子线程被激活后就去获取buf中的字符长度,
	//然后打印;
	//完成打印后,再次被阻塞
	
	sleep(1);
	while(flag == 0)
	{
		pthread_mutex_lock(&mutex);	//上锁
		printf("本次输入了%d个字符\n", (int)strlen(buf));
		memset(buf, 0, sizeof(buf));  
		pthread_mutex_unlock(&mutex);    //解锁
		sleep(1);
	}

	pthread_exit(NULL);     //线程退出
}


int main(void)
{
	int ret = -1;
	pthread_t th = -1;
	
	pthread_mutex_init(&mutex, NULL);	//初始化信号量
	
	ret = pthread_create(&th, NULL, func, NULL);	//创建子线程
	if (ret != 0)
	{
		printf("pthread_create error.\n");
		exit(-1);
	}
	
	printf("输入一个字符串,以回车结束\n");
	
	while(1)
	{
		pthread_mutex_lock(&mutex);   //上锁
		scanf("%s", buf);
		pthread_mutex_unlock(&mutex);   //解锁
  
		//去比较用户输入的是不是end,如果是则退出,如果不是则继续
		if (!strncmp(buf, "end", 3))
		{
			printf("程序结束\n");
			flag = 1;
			//exit(0);
			break;
		}
		sleep(1);
	}
	
	printf("等待回收子线程\n");
	ret = pthread_join(th, NULL);	//回收子线程
	if (ret != 0)
	{
		printf("pthread_join error.\n");
		exit(-1);
	}
	printf("子线程回收成功\n");
	
	pthread_mutex_destroy(&mutex);	//销毁信号量
	
	return 0;
}

注:这个程序实例仅为了学习如何使用互斥锁来实现关键段代码保护,知晓大概流程及实现机理,实用意义不大。

 

5、线程同步之条件变量

5.1、相关函数

  • pthread_cond_init
  • pthread_cond_destroy
  • pthread_cond_wait
  • pthread_cond_signal 
  • pthread_cond_broadcast

 

5.2、使用条件变量来实现4.2中的代码

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


char buf[200] = {0};
pthread_mutex_t mutex;	//定义一个信号量
pthread_cond_t cond;	//定义一个条件变量
unsigned char flag = 0;	//定义一个标志位

//子线程程序,作用是统计buf中的字符个数并打印
void * func(void * arg)
{
	//子线程首先应该有个循环
	//循环中阻塞在等待主线程激活的时候,子线程被激活后就去获取buf中的字符长度,
	//然后打印;
	//完成打印后,再次被阻塞
	
	while(flag == 0)
	{
		pthread_mutex_lock(&mutex);	//上锁
		pthread_cond_wait(&cond, &mutex);	//阻塞在这里,等待条件信号到来		
		printf("本次输入了%d个字符\n", (int)strlen(buf));
		memset(buf, 0, sizeof(buf));
		pthread_mutex_unlock(&mutex);  //解锁
	}

	pthread_exit(NULL);  //线程退出
}


int main(void)
{
	int ret = -1;
	pthread_t th = -1;
	
	pthread_mutex_init(&mutex, NULL);	//初始化信号量
	pthread_cond_init(&cond, NULL);	   //条件变量初始化
	
	ret = pthread_create(&th, NULL, func, NULL);	//创建子线程
	if (ret != 0)
	{
		printf("pthread_create error.\n");
		exit(-1);
	}
	
	printf("输入一个字符串,以回车结束\n");
	
	while(1)
	{
		scanf("%s", buf);
		pthread_cond_signal(&cond);	//发送条件变量信号

		//去比较用户输入的是不是end,如果是则退出,如果不是则继续
		if (!strncmp(buf, "end", 3))
		{
			printf("程序结束\n");
			flag = 1;
			break;
		}
	}

	printf("等待回收子线程\n");
	ret = pthread_join(th, NULL);	//回收子线程
	if (ret != 0)
	{
		printf("pthread_join error.\n");
		exit(-1);
	}
	printf("子线程回收成功\n");
	
	pthread_mutex_destroy(&mutex);	//销毁信号量
	pthread_cond_destroy(&cond);	//销毁条件变量信号
	
	return 0;
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章