1. 程式人生 > >執行緒同步:互斥鎖,訊號量

執行緒同步:互斥鎖,訊號量

同步概念:

當多個執行緒共享相同的一塊記憶體時(實際上在一個程序的各個執行緒之間,除了棧區的資料之外,其他的資料這幾個縣城之間都是相互共享的),需要確保每個執行緒看到一致的資料檢視。也就是說,這些執行緒在對資料進行操作時,應該是同步的,也就是說當一個執行緒正在操作一個數據時,其他執行緒無法同時對該資料進行操作(讀資料除外,因為讀資料不會改動資料的內容)。這就是所謂的執行緒同步。

實現機制:

  • 訊號量。

訊號量大致可以分為兩種,一種取自POSIX的實時擴充套件,作用於執行緒。另外一種被稱為系統V訊號量,作用於程序的同步控制。訊號量是一個特殊的變數,它可以被增加或者減少,但對其的關鍵訪問被保證是原子操作

,即使在一個多執行緒程式中也是如此,這意味著如果一個程式中有兩個或者多個執行緒試圖改變一個訊號量的值,系統將保證所有的操作都將依次進行。

1.訊號量的建立

這個函式初始化由sem指向的訊號量物件,pshread引數控制訊號量的型別,如果其值為0,就表示這個訊號量只能當前程序區域性使用,否則,這個訊號量就可以在多個程序之間共享。但是現在的Linux系統花不支援這種共享,給pshared傳遞一個非0值將導致函式呼叫失敗!

2.訊號量的加減操作

                    sem_wait()函式表示對該訊號量進行減操作(p操作),引數為訊號量的id所在的地址。

                    sem_post()函式表示對該訊號量進行加操作(v操作),引數為訊號量的id所在的地址。

3.訊號量的銷燬

                  和前面的函式相同,這個函式的引數也是訊號量的id所在的地址。

  • 互斥鎖。pthread執行緒庫有一個互斥介面,可以確保同意時間只有一個執行緒訪問資料。互斥量從本質上說是一把鎖,在訪問共享資源前對互斥量進行加鎖,訪問完成後釋放互斥量上的鎖。對互斥量進行加鎖後,其他任何試圖再次對互斥量進行加鎖的執行緒都會阻塞,直到當前執行緒釋放該互斥鎖

    。如果釋放該互斥鎖時有多個執行緒阻塞,所有阻塞的執行緒都會變成可執行狀態,第一個變成可執行狀態的先成功可以對該互斥量進行加鎖,其他執行緒看到互斥量又被鎖住,只能回去再次等待他重新變成可用的。在這種方式下,每次只有一個執行緒可以向前執行,這就保證了執行緒之間的同步,對該資源的訪問就會變得有序起來。

1.互斥鎖的實現

互斥變數用pthread_mutex_t資料型別來表示,在使用護持變數之前必須先對其進行初始化。可通過呼叫pthread_mutex_init函式進行初始化,在釋放記憶體前需要呼叫pthread_mutex_destroy函式進行釋放。

第一個引數就是我們申請的護持變數的地址,如果要使用預設的屬性初始化互斥量,只需要把attr引數設定為NULL。而我們大多書情況下也都是這樣做的。

2.給互斥量加鎖,解鎖

對互斥量進行加鎖,需要呼叫pthread_mutex_lock函式,如果互斥量已經上鎖,則呼叫該函式的執行緒將阻塞到互斥量被解鎖。對互斥量進行解鎖,要呼叫pthread_mutex_unlock.

如果執行緒不希望被阻塞,它可以使用pthread_mutex_trylock嘗試對互斥量進行加鎖,如果呼叫該函式時互斥量未被加鎖,那麼該函式將對互斥量進行加鎖,不會阻塞並返回0,否則該函式呼叫失敗,返回一個EBUSY的巨集。

訊號量示例:

使用兩個訊號量實現兩個執行緒的交替列印。

 

/*************************************************************************
    > File Name: main.c
    > Created Time: Fri 26 Oct 2018 08:14:50 PM CST
 ************************************************************************/

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

#include<pthread.h>
#include<semaphore.h>


sem_t sem1;
sem_t sem2;


static int flag = 0;

void *func(void *ptr)
{
	int i = 0;
	for(; i < 5; i++)
	{
		sem_wait(&sem2);
		printf("func running\n");
		sem_post(&sem1);
	}
	flag = 1;
	printf("func return\n");
	sem_post(&sem1);

}

int main()
{

	sem_init(&sem1, 0,1);
	sem_init(&sem2,0,1);

	pthread_t pth;
	int err = pthread_create(&pth, NULL,func,NULL);
	assert(err == 0 );

	int i = 0;
	for(; i < 8; i++)
	{
		if(flag == 0)
			sem_wait(&sem1);
		printf("main running\n");
		if(flag == 0)
			sem_post(&sem2);
	}
	pthread_exit(NULL);

	return 0;
}

 互斥鎖示例:

        同樣實現交替列印;

/*************************************************************************
    > File Name: main.c
    > Created Time: Fri 26 Oct 2018 08:14:50 PM CST
 ************************************************************************/

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

#include<pthread.h>
#include<semaphore.h>


pthread_mutex_t mutex1;
pthread_mutex_t mutex2;


static int flag = 0;

void *func(void *ptr)
{
	int i = 0;
	for(; i < 5; i++)
	{
		pthread_mutex_lock(&mutex1);
		printf("func running\n");
		pthread_mutex_unlock(&mutex2);
	}
	flag = 1;
	printf("func return\n");
	pthread_mutex_unlock(&mutex2);
}

int main()
{

	int err1 = pthread_mutex_init(&mutex1,NULL);
	assert(err1 == 0);
	int err2 = pthread_mutex_init(&mutex2,NULL);
	assert(err2 == 0);
	
	pthread_t pth;
	int err = pthread_create(&pth, NULL,func,NULL);
	assert(err == 0 );

	int i = 0;
	for(; i < 8; i++)
	{
		if(flag == 0)
			pthread_mutex_lock(&mutex2);
		printf("main running\n");
		if(flag == 0)
			pthread_mutex_unlock(&mutex1);
	}
	pthread_exit(NULL);

	return 0;
}