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;
}

 

 

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