前言:通過一個任務導入並分析線程同步的信號量。
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;
}