一個看起來簡單而實際涉及很多細節的關於多執行緒程式設計的例項,文章主要以該例項展開講解,例程如下

是否熟悉POSIX多執行緒程式設計技術?如熟悉,編寫程式完成如下功能:

1有一int型全域性變數g_Flag初始值為0

2在主線稱中起動執行緒1,列印“thisis thread1”,並將g_Flag設定為1

3在主線稱中啟動執行緒2,列印“thisis thread2”,並將g_Flag設定為2

4執行緒序2需要線上程1退出後才能退出

5主執行緒在檢測到g_Flag1變為2,或者從2變為1的時候退出

主要介紹了:

a)        程序與執行緒;

b)       使用執行緒的理由;

c)        有關執行緒操作的函式;

d)       執行緒之間的互斥;

e)        執行緒之間的同步;

f)        試題最終程式碼

程式設計測試過程遇到的問題:

1)編譯過程出現錯誤:undefined reference to `pthread_create',原因是pthread庫不是標準linux庫。編譯選項應加上- lpthread,若要加入-o選項指定輸出檔名,則必須將- lpthread選項置於最後,即gcc (-o target) X.C –lpthread

2)定義了全域性變數g_flag = 0,主執行緒main函式中分別呼叫了兩個子執行緒thread1,thread2,兩個子執行緒函式中分別修改g_flag的值為1,2。然而,g_flag值改變前的輸出總是0,而不是前一個執行緒修改後的值,也就是說全域性變數在兩個子執行緒執行後其值不變。為了驗證猜想,在主執行緒函式main的兩個子執行緒之間加入了語句g_flag= 3,其執行情況如下。


以上,確實表明兩個子執行緒對全域性變數g_flag值的修改在退出各自執行緒後失效了。Why?有這樣一種可能,兩個執行緒在列印第一條內容時,均沒有其他執行緒對g_flag進行修改。(不過這就太巧了,兩個執行緒應該嚴格按照交叉的順序來執行,但我嘗試了多次列印,竟沒有一次按預期發生的,這還是巧合嗎?很奇怪)

3)為了防止thread1與thread2同時對g_flag進行修改,使用了thread_mutex_lock執行緒鎖對程式碼臨界段進行鎖定(這裡是修改g_flag值的過程)以禁止其他執行緒訪問同一資源;針對例程第四條需求,線上程thread2退出前使用了pthread_join函式阻塞其退出直至thread1先退出;針對例程第五條需求有使用條件變數可以以原子方式阻塞執行緒,直到某個特定條件為真為止。條件變數始終與互斥鎖一起使用。對條件的測試是在互斥鎖(互斥)的保護下進行的”。因此,在主執行緒main函式中使用了pthread_cond_wait以阻塞主執行緒main函式退出直至條件變數cond為真,而pthread_cond_signal則用於發出條件變數為真的訊號。擷取程式碼的三次執行表現如下:


以上,可以看出程式第一次執行,thread2執行前thread1修改了g_flag的值,然而第二次執行時,thread2執行前thread1並未修改g_flag的值。

此外,這裡條件變數為真的判斷及pthread_cond_signal訊號的發出之所以與thread_mutex_lock互斥鎖一起使用,並在互斥鎖(互斥)的保護下進行,是因為“某個特性條件”通常是在多個執行緒之間共享的某個變數。互斥鎖允許這個變數可以在不同的執行緒中設定和檢測。

4)對於例程第五條需求,個人認為部落格文章中並沒有嚴格實現,我理解的需求應是thread1或是thread2僅有其一可修改g_flag值,然後主執行緒檢測到訊號cong為真即退出。文章的實現方式如下:


通過加入else分支,使g_flag僅可被修改一次,


執行結果也很有意思,


從第一次執行情況來看thread2在第一次輸出g_flag值時,其值為0;而接下來的if判斷時,其值已經同步了thread2對g_flag的修改,g_flag = 1。這樣,似乎確實這麼巧,也驗證了2)中的猜測。

下面是本文測試的最終程式碼:

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

int g_flag = 0;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

void *func1(void *);
void *func2(void *);

int main(int argc, char **argv)
{
    printf("[main] enter\n");
    pthread_t tid1, tid2;
	int rc1 = 0, rc2 = 0;
	
	rc1 = pthread_create( &tid1, NULL, func1, NULL );
	if(0 != rc1)
	{
	    printf("%s: %d\n", __func__, strerror(rc1));
	}
   
    //sleep(5);  //單位s
    //g_flag = 3;

    rc2 = pthread_create( &tid2, NULL, func2, &tid1 );
	if(0 != rc2)
	{
	    printf("%s: %d\n", __func__, strerror(rc2));
	}

    pthread_cond_wait( &cond, &mutex );  //阻塞執行緒直至cond為真
	
	printf("[main] leave\n");
	exit(0);
}

void *func1(void *arg)
{
    printf("[func1] enter\n");
	printf("this is thread1, g_flag = %d, t_id:%u\n", g_flag, (unsigned int)pthread_self());

    pthread_mutex_lock( &mutex );
    if(2 == g_flag)
	{
	    printf("thread2 has made g_flag = 2\n");
		pthread_cond_signal( &cond );
	}
	else
	{
	    g_flag = 1;
	}
	pthread_mutex_unlock( &mutex );

	printf("this is thread1, g_flag = %d, t_id:%u\n", g_flag, (unsigned int)pthread_self());
	printf("[func1] leave\n");
	pthread_exit(0);
    //exit(0);
}

void *func2(void *arg)
{
    printf("[func2] enter\n");
	printf("this is thread2, g_flag = %d, t_id:%u\n", g_flag, (unsigned int)pthread_self());
	
	pthread_mutex_lock( &mutex );
	if(1 == g_flag)
    {   
	    printf("thread1 has made g_flag = 1\n");
	    pthread_cond_signal( &cond );
    }
	else
	{
        g_flag = 2;
	}
    pthread_mutex_unlock( &mutex );
	
	printf("this is thread2, g_flag = %d, t_id:%u\n", g_flag, (unsigned int)pthread_self());
	pthread_join( *(pthread_t *)arg, NULL );
	printf("[func2] leave\n");
	pthread_exit(0);
    //exit(0);
}

由於初次接觸多執行緒程式設計,文中難免會有理解不到位的地方