Windows XP Windows 7 Windows 2003 Windows Vista Windows教程綜合 Linux 系統教程
Windows 10 Windows 8 Windows 2008 Windows NT Windows Server 電腦軟件教程
 Windows教程網 >> Linux系統教程 >> Linux教程 >> 使用信號量控制Linux線程同步

使用信號量控制Linux線程同步

日期:2017/2/7 14:25:09      編輯:Linux教程
 

線程同步
在現實生活中,有些東西就必須是按順序執行的,只有我完成了以後,你才能在我的勞動成果上接著干;不能我還沒有完成,你就開始干活了。這就是線程同步最直白的解釋了。

在進行程序設計時,亦是如此。線程同步,同步的是什麼?它同步的是對共享資源(內存區域,公共變量等)或者臨界區域的訪問。有的時候,這些共享資源和臨界區域,就只能容忍一個線程對它進行操作(讀或者寫,讀操作一般不控制,主要是寫操作),這個時候,我們必須要對這些共享資源或者臨界區域進行同步,那麼如何對它們進行線程同步呢?

在Linux中主要提供了以下兩種方法:

用信號量進行同步
用互斥量進行同步
不管用什麼方法,都是為了線程同步,而這篇文章就對信號量進行線程同步進行詳細的總結。

什麼是信號量
首先來搞清楚信號量的概念。信號量是一個特殊類型的變量,它可以被增加或減少,但對它的訪問都會被保證是原子操作,即使在一個多線程程序中也是如此。也就是說,如果一個程序中有兩個或多個線程試圖改變一個信號量的值,系統將保證所有的操作都將依次進行。如果換成普通的變量,來自同一個程序中的不同線程的沖突操作將會導致不確定的操作。

在工作中,你會遇到兩種信號量:二進制信號量和計數信號量。二進制信號量只有0和1兩種取值,而計數信號量則有更大的取值范圍。如果某個共享資源只能被一個線程訪問,那麼二進制信號量則是最好的打算;如果有多個線程需要訪問共享資源呢,使用計數信號量則是個好的主意。

信號量相關API
在Linux中已經提供了一些相關的API接口來實現信號量,先來和這些接口混一個臉熟,之後再通過一個小例子來熟悉如何使用這些接口。

/**
* 創建信號量
* @param setm_t* sem 信號量對象,完成對該對象的初始化
* @param int pshared 信號量的類型;如果其值為0,就表示這個信號量是當前進程的局部信號量;否則,這個信號量就可以在多個進程之間共享
* @param unsigned int value 信號量的初始值
* @return int 成功時返回0;失敗時返回-1
*/int sem_init(sem_t *sem, int pshared, unsigned int value);/**
* 以原子操作的方式將信號量的值減1
* @param sem_t* 調用sem_init得到的信號量對象
* @return int 成功時返回0;失敗時返回-1
*/int sem_wait(sem_t *sem);/**
* 以原子操作的方式將信號量的值加1
* @param sem_t* 調用sem_init得到的信號量對象
* @return int 成功時返回0;失敗時返回-1
*/int sem_post(sem_t *sem);/**
* 獲得信號量的計數值
* @param sem_t* sem 調用sem_init得到的信號量對象
* @param int& value 實際返回的信號量的值
* @return int 成功時返回0;失敗時返回-1
*/int sem_getvalue(sem_t *sem, int &value);/**
* 清理該信號量擁有的所有資源;如果企圖清理的信號量正被一些線程等待,就會收到錯誤
* @param sem_t* 調用sem_init得到的信號量對象
* @return int 成功時返回0;失敗時返回-1
*/int sem_destroy(sem_t *sem);
sem_wait會對信號量的值進行減1操作,但是它會等到信號量的值大於等於1時,才開始進行減1的操作。所以,如果對值為2的信號量調用sem_wait,線程會繼續執行;而如果對為0的信號量進行sem_wait操作,這個函數就會等待,直到有其它線程增加了該信號量的值使其不再為0為止。

一個小實例
關於信號量操作的API函數都已經介紹完了,現在就通過一個小的程序來用一用這些函數,通過實際的編碼,能更好的掌握如何使用這些函數。

接下來代碼要完成這樣一個功能:

主線程從標准輸入終端讀取輸入的內容;
子線程1將主線程中輸入的內容轉換成大寫。
來吧,我們就按照上面的功能來編碼實現一下。功能簡單,我准備這樣實現:

在主線程初始化信號量為0;
當主線程輸入完成以後,調用sem_post增加信號量的值;
在子線程1中,調用sem_wait,等到信號量的值為1時,就減少信號量的值,然後執行線程中的代碼。
好了,編碼吧。

#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <pthread.h>#include <semaphore.h>void *threadFunc(void *arg);sem_t sem;#define WORK_SIZE 1024char work_area[WORK_SIZE]; // 主線程和子線程都訪問的共享資源int main(){
int res;
pthread_t upperThread;
void *threadRes;
res = sem_init(&sem, 0, 0); // 信號量初始化為0

if (res != 0)
{
perror("Semaphore initialization failed.");
exit(EXIT_FAILURE);
}

res = pthread_create(&upperThread, NULL, threadFunc, NULL);
if (res != 0)
{
perror("Thread creation failed.");
exit(EXIT_FAILURE);
}
printf("Please input convert text, then enter 'end' to finish.\n");
while (strncmp("end", work_area, 3) != 0)
{
fgets(work_area, WORK_SIZE, stdin);
sem_post(&sem); // 輸入完成,增加信號量的值
int semValue = 0;
res = sem_getvalue(&sem, &semValue); // 獲得信號量當前的值
printf("Semaphore count: %d\n", semValue);
}

printf("\nWaiting for thread to finish...\n");
res = pthread_join(upperThread, &threadRes);
if (res != 0)
{
perror("Thread join failed.");
exit(EXIT_FAILURE);
}
printf("Thread joined.\n");
sem_destroy(&sem);
exit(EXIT_SUCCESS);}void *threadFunc(void *arg){
sem_wait(&sem); // 減少信號量的值
int semValue = 0;
sem_getvalue(&sem, &semValue);
printf("Semaphore count: %d\n", semValue);

while (strncmp("end", work_area, 3) != 0)
{
for (int i = 0; work_area[i] != '\0'; ++i)
{
if (work_area[i] >= 'a' && work_area[i] <= 'z')
{
work_area[i] = work_area[i] - 32;
}
}

printf("After convtered: %s\n", work_area);
sem_wait(&sem); // 再次阻塞,等待輸入
}
pthread_exit(NULL);}

Copyright © Windows教程網 All Rights Reserved