亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 服務器 > Linux服務器 > 正文

詳解Linux多線程編程(不限Linux)

2024-09-05 23:03:06
字體:
來源:轉載
供稿:網友

前言

線程?為什么有了進程還需要線程呢,他們有什么區別?使用線程有什么優勢呢?還有多線程編程的一些細節問題,如線程之間怎樣同步、互斥,這些東西將在本文中介紹。我在某QQ群里見到這樣一道面試題:

是否熟悉POSIX多線程編程技術?如熟悉,編寫程序完成如下功能:

1)有一int型全局變量g_Flag初始值為0;

2) 在主線稱中起動線程1,打印“this is thread1”,并將g_Flag設置為1

3) 在主線稱中啟動線程2,打印“this is thread2”,并將g_Flag設置為2

4) 線程序1需要在線程2退出后才能退出

5) 主線程在檢測到g_Flag從1變為2,或者從2變為1的時候退出

我們帶著這題開始這篇文章,結束之后,大家就都會做了。本文的框架如下:

1、進程與線程

進程是程序執行時的一個實例,即它是程序已經執行到何種程度的數據結構的匯集。從內核的觀點看,進程的目的就是擔當分配系統資源(CPU時間、內存等)的基本單位。

線程是進程的一個執行流,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位。一個進程由幾個線程組成(擁有很多相對獨立的執行流的用戶程序共享應用程序的大部分數據結構),線程與同屬一個進程的其他的線程共享進程所擁有的全部資源。

"進程——資源分配的最小單位,線程——程序執行的最小單位"

進程有獨立的地址空間,一個進程崩潰后,在保護模式下不會對其它進程產生影響,而線程只是一個進程中的不同執行路徑。線程有自己的堆棧和局部變量,但線程沒有單獨的地址空間,一個線程死掉就等于整個進程死掉,所以多進程的程序要比多線程的程序健壯,但在進程切換時,耗費資源較大,效率要差一些。但對于一些要求同時進行并且又要共享某些變量的并發操作,只能用線程,不能用進程。

2、使用線程的理由

從上面我們知道了進程與線程的區別,其實這些區別也就是我們使用線程的理由。總的來說就是:進程有獨立的地址空間,線程沒有單獨的地址空間(同一進程內的線程共享進程的地址空間)。(下面的內容摘自Linux下的多線程編程)

使用多線程的理由之一是和進程相比,它是一種非常"節儉"的多任務操作方式。我們知道,在Linux系統下,啟動一個新的進程必須分配給它獨立的地址空間,建立眾多的數據表來維護它的代碼段、堆棧段和數據段,這是一種"昂貴"的多任務工作方式。而運行于一個進程中的多個線程,它們彼此之間使用相同的地址空間,共享大部分數據,啟動一個線程所花費的空間遠遠小于啟動一個進程所花費的空間,而且,線程間彼此切換所需的時間也遠遠小于進程間切換所需要的時間。據統計,總的說來,一個進程的開銷大約是一個線程開銷的30倍左右,當然,在具體的系統上,這個數據可能會有較大的區別。

使用多線程的理由之二是線程間方便的通信機制。對不同進程來說,它們具有獨立的數據空間,要進行數據的傳遞只能通過通信的方式進行,這種方式不僅費時,而且很不方便。線程則不然,由于同一進程下的線程之間共享數據空間,所以一個線程的數據可以直接為其它線程所用,這不僅快捷,而且方便。當然,數據的共享也帶來其他一些問題,有的變量不能同時被兩個線程所修改,有的子程序中聲明為static的數據更有可能給多線程程序帶來災難性的打擊,這些正是編寫多線程程序時最需要注意的地方。

除了以上所說的優點外,不和進程比較,多線程程序作為一種多任務、并發的工作方式,當然有以下的優點:

  • 提高應用程序響應。這對圖形界面的程序尤其有意義,當一個操作耗時很長時,整個系統都會等待這個操作,此時程序不會響應鍵盤、鼠標、菜單的操作,而使用多線程技術,將耗時長的操作(time consuming)置于一個新的線程,可以避免這種尷尬的情況。
  • 使多CPU系統更加有效。操作系統會保證當線程數不大于CPU數目時,不同的線程運行于不同的CPU上。
  • 改善程序結構。一個既長又復雜的進程可以考慮分為多個線程,成為幾個獨立或半獨立的運行部分,這樣的程序會利于理解和修改。

=============================

從函數調用上來說,進程創建使用fork()操作;線程創建使用clone()操作。Richard Stevens大師這樣說過:

fork is expensive. Memory is copied from the parent to the child, all descriptors are duplicated in the child, and so on. Current implementations use a technique called copy-on-write, which avoids a copy of the parent's data space to the child until the child needs its own copy. But, regardless of this optimization, fork is expensive.

IPC is required to pass information between the parent and child after the fork. Passing information from the parent to the child before the fork is easy, since the child starts with a copy of the parent's data space and with a copy of all the parent's descriptors. But, returning information from the child to the parent takes more work.

Threads help with both problems. Threads are sometimes called lightweight processes since a thread is "lighter weight" than a process. That is, thread creation can be 10–100 times faster than process creation.

All threads within a process share the same global memory. This makes the sharing of information easy between the threads, but along with this simplicity comes the problem of synchronization.

=============================

3、有關線程操作的函數

#include <pthread.h>int pthread_create(pthread_t *tid, const pthread_attr_t *attr, void *(*func) (void *), void *arg);int pthread_join (pthread_t tid, void ** status);pthread_t pthread_self (void);int pthread_detach (pthread_t tid);void pthread_exit (void *status);

pthread_create用于創建一個線程,成功返回0,否則返回Exxx(為正數)。

  • pthread_t *tid:線程id的類型為pthread_t,通常為無符號整型,當調用pthread_create成功時,通過*tid指針返回。
  • const pthread_attr_t *attr:指定創建線程的屬性,如線程優先級、初始棧大小、是否為守護進程等。可以使用NULL來使用默認值,通常情況下我們都是使用默認值。
  • void *(*func) (void *):函數指針func,指定當新的線程創建之后,將執行的函數。
  • void *arg:線程將執行的函數的參數。如果想傳遞多個參數,請將它們封裝在一個結構體中。

pthread_join用于等待某個線程退出,成功返回0,否則返回Exxx(為正數)。

  • pthread_t tid:指定要等待的線程ID
  • void ** status:如果不為NULL,那么線程的返回值存儲在status指向的空間中(這就是為什么status是二級指針的原因!這種才參數也稱為“值-結果”參數)。

pthread_self用于返回當前線程的ID。

pthread_detach用于是指定線程變為分離狀態,就像進程脫離終端而變為后臺進程類似。成功返回0,否則返回Exxx(為正數)。變為分離狀態的線程,如果線程退出,它的所有資源將全部釋放。而如果不是分離狀態,線程必須保留它的線程ID,退出狀態直到其它線程對它調用了pthread_join。

進程也是類似,這也是當我們打開進程管理器的時候,發現有很多僵死進程的原因!也是為什么一定要有僵死這個進程狀態。

pthread_exit用于終止線程,可以指定返回值,以便其他線程通過pthread_join函數獲取該線程的返回值。

  • void *status:指針線程終止的返回值。

知道了這些函數之后,我們試圖來完成本文一開始的問題:

1)有一int型全局變量g_Flag初始值為0;

2)在主線稱中起動線程1,打印“this is thread1”,并將g_Flag設置為1

3)在主線稱中啟動線程2,打印“this is thread2”,并將g_Flag設置為2

這3點很簡單嘛!?。〔痪褪钦{用pthread_create創建線程。代碼如下:

/* * 1)有一int型全局變量g_Flag初始值為0; * * 2)在主線稱中起動線程1,打印“this is thread1”,并將g_Flag設置為1 * * 3)在主線稱中啟動線程2,打印“this is thread2”,并將g_Flag設置為2 * */#include<stdio.h>#include<stdlib.h>#include<pthread.h>#include<errno.h>#include<unistd.h>int g_Flag=0;void* thread1(void*);void* thread2(void*);/* * when program is started, a single thread is created, called the initial thread or main thread. * Additional threads are created by pthread_create. * So we just need to create two thread in main(). */int main(int argc, char** argv){ printf("enter main/n"); pthread_t tid1, tid2; int rc1=0, rc2=0; rc2 = pthread_create(&tid2, NULL, thread2, NULL); if(rc2 != 0) printf("%s: %d/n",__func__, strerror(rc2)); rc1 = pthread_create(&tid1, NULL, thread1, &tid2); if(rc1 != 0) printf("%s: %d/n",__func__, strerror(rc1)); printf("leave main/n"); exit(0); }/* * thread1() will be execute by thread1, after pthread_create() * it will set g_Flag = 1; */void* thread1(void* arg){ printf("enter thread1/n"); printf("this is thread1, g_Flag: %d, thread id is %u/n",g_Flag, (unsigned int)pthread_self()); g_Flag = 1; printf("this is thread1, g_Flag: %d, thread id is %u/n",g_Flag, (unsigned int)pthread_self()); printf("leave thread1/n"); pthread_exit(0);}/* * thread2() will be execute by thread2, after pthread_create() * it will set g_Flag = 2; */void* thread2(void* arg){ printf("enter thread2/n"); printf("this is thread2, g_Flag: %d, thread id is %u/n",g_Flag, (unsigned int)pthread_self()); g_Flag = 2; printf("this is thread1, g_Flag: %d, thread id is %u/n",g_Flag, (unsigned int)pthread_self()); printf("leave thread2/n"); pthread_exit(0);}

這樣就完成了1)、2)、3)這三點要求。編譯執行得如下結果:

netsky@ubuntu:~/workspace/pthead_test$ gcc -lpthread test.c 

如果程序中使用到了pthread庫中的函數,除了要#include<pthread.h>,在編譯的時候還有加上-lpthread 選項。

netsky@ubuntu:~/workspace/pthead_test$ ./a.out enter main enter thread2 this is thread2, g_Flag: 0, thread id is 3079588720 this is thread1, g_Flag: 2, thread id is 3079588720 leave thread2 leave main enter thread1 this is thread1, g_Flag: 2, thread id is 3071196016 this is thread1, g_Flag: 1, thread id is 3071196016 leave thread1 

但是運行結果不一定是上面的,還有可能是:

netsky@ubuntu:~/workspace/pthead_test$ ./a.out enter main leave main enter thread1 this is thread1, g_Flag: 0, thread id is 3069176688 this is thread1, g_Flag: 1, thread id is 3069176688 leave thread1 

或者是:

netsky@ubuntu:~/workspace/pthead_test$ ./a.out enter main leave main 

等等。這也很好理解因為,這取決于主線程main函數何時終止,線程thread1、thread2是否能夠來得急執行它們的函數。這也是多線程編程時要注意的問題,因為有可能一個線程會影響到整個進程中的所有其它線程!如果我們在main函數退出前,sleep()一段時間,就可以保證thread1、thread2來得及執行。

Attention:大家肯定已經注意到了,我們在線程函數thread1()、thread2()執行完之前都調用了pthread_exit。如果我是調用exit()又或者是return會怎樣呢?自己動手試試吧!

  • pthread_exit()用于線程退出,可以指定返回值,以便其他線程通過pthread_join()函數獲取該線程的返回值。
  • return是函數返回,只有線程函數return,線程才會退出。
  • exit是進程退出,如果在線程函數中調用exit,進程中的所有函數都會退出!

“4) 線程序1需要在線程2退出后才能退出”第4點也很容易解決,直接在thread1的函數退出之前調用pthread_join就OK了。

4、線程之間的互斥

上面的代碼似乎很好的解決了問題的前面4點要求,其實不然?。。∫驗間_Flag是一個全局變量,線程thread1和thread2可以同時對它進行操作,需要對它進行加鎖保護,thread1和thread2要互斥訪問才行。下面我們就介紹如何加鎖保護——互斥鎖。

互斥鎖:

使用互斥鎖(互斥)可以使線程按順序執行。通常,互斥鎖通過確保一次只有一個線程執行代碼的臨界段來同步多個線程?;コ怄i還可以保護單線程代碼。

互斥鎖的相關操作函數如下:

#include <pthread.h> int pthread_mutex_lock(pthread_mutex_t * mptr); int pthread_mutex_unlock(pthread_mutex_t * mptr); //Both return: 0 if OK, positive Exxx value on error

在對臨界資源進行操作之前需要pthread_mutex_lock先加鎖,操作完之后pthread_mutex_unlock再解鎖。而且在這之前需要聲明一個pthread_mutex_t類型的變量,用作前面兩個函數的參數。具體代碼見第5節。

5、線程之間的同步

第5點——主線程在檢測到g_Flag從1變為2,或者從2變為1的時候退出。就需要用到線程同步技術!線程同步需要條件變量。

條件變量:

使用條件變量可以以原子方式阻塞線程,直到某個特定條件為真為止。條件變量始終與互斥鎖一起使用。對條件的測試是在互斥鎖(互斥)的保護下進行的。

如果條件為假,線程通常會基于條件變量阻塞,并以原子方式釋放等待條件變化的互斥鎖。如果另一個線程更改了條件,該線程可能會向相關的條件變量發出信號,從而使一個或多個等待的線程執行以下操作:

  • 喚醒
  • 再次獲取互斥鎖
  • 重新評估條件

在以下情況下,條件變量可用于在進程之間同步線程:

  • 線程是在可以寫入的內存中分配的
  • 內存由協作進程共享

“使用條件變量可以以原子方式阻塞線程,直到某個特定條件為真為止。”即可用到第5點,主線程main函數阻塞于等待g_Flag從1變為2,或者從2變為1。條件變量的相關函數如下:

#include <pthread.h> int pthread_cond_wait(pthread_cond_t *cptr, pthread_mutex_t *mptr); int pthread_cond_signal(pthread_cond_t *cptr); //Both return: 0 if OK, positive Exxx value on error

pthread_cond_wait用于等待某個特定的條件為真,pthread_cond_signal用于通知阻塞的線程某個特定的條件為真了。在調用者兩個函數之前需要聲明一個pthread_cond_t類型的變量,用于這兩個函數的參數。

為什么條件變量始終與互斥鎖一起使用,對條件的測試是在互斥鎖(互斥)的保護下進行的呢?因為“某個特性條件”通常是在多個線程之間共享的某個變量?;コ怄i允許這個變量可以在不同的線程中設置和檢測。

通常,pthread_cond_wait只是喚醒等待某個條件變量的一個線程。如果需要喚醒所有等待某個條件變量的線程,需要調用:

int pthread_cond_broadcast (pthread_cond_t * cptr);

默認情況下面,阻塞的線程會一直等待,知道某個條件變量為真。如果想設置最大的阻塞時間可以調用:

int pthread_cond_timedwait (pthread_cond_t * cptr, pthread_mutex_t *mptr, const struct timespec *abstime);

如果時間到了,條件變量還沒有為真,仍然返回,返回值為ETIME。

6、試題最終代碼

通過前面的介紹,我們可以輕松的寫出代碼了,如下所示:

/* 是否熟悉POSIX多線程編程技術?如熟悉,編寫程序完成如下功能: 1)有一int型全局變量g_Flag初始值為0; 2)在主線稱中起動線程1,打印“this is thread1”,并將g_Flag設置為1 3)在主線稱中啟動線程2,打印“this is thread2”,并將g_Flag設置為2 4)線程序1需要在線程2退出后才能退出 5)主線程在檢測到g_Flag從1變為2,或者從2變為1的時候退出 */#include<stdio.h>#include<stdlib.h>#include<pthread.h>#include<errno.h>#include<unistd.h>typedef void* (*fun)(void*);int g_Flag=0;static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;void* thread1(void*);void* thread2(void*);/* * when program is started, a single thread is created, called the initial thread or main thread. * Additional threads are created by pthread_create. * So we just need to create two thread in main(). */int main(int argc, char** argv){ printf("enter main/n"); pthread_t tid1, tid2; int rc1=0, rc2=0; rc2 = pthread_create(&tid2, NULL, thread2, NULL); if(rc2 != 0) printf("%s: %d/n",__func__, strerror(rc2)); rc1 = pthread_create(&tid1, NULL, thread1, &tid2); if(rc1 != 0) printf("%s: %d/n",__func__, strerror(rc1)); pthread_cond_wait(&cond, &mutex); printf("leave main/n"); exit(0); }/* * thread1() will be execute by thread1, after pthread_create() * it will set g_Flag = 1; */void* thread1(void* arg){ printf("enter thread1/n"); printf("this is thread1, g_Flag: %d, thread id is %u/n",g_Flag, (unsigned int)pthread_self()); pthread_mutex_lock(&mutex); if(g_Flag == 2) pthread_cond_signal(&cond); g_Flag = 1; printf("this is thread1, g_Flag: %d, thread id is %u/n",g_Flag, (unsigned int)pthread_self()); pthread_mutex_unlock(&mutex); pthread_join(*(pthread_t*)arg, NULL); printf("leave thread1/n"); pthread_exit(0);}/* * thread2() will be execute by thread2, after pthread_create() * it will set g_Flag = 2; */void* thread2(void* arg){ printf("enter thread2/n"); printf("this is thread2, g_Flag: %d, thread id is %u/n",g_Flag, (unsigned int)pthread_self()); pthread_mutex_lock(&mutex); if(g_Flag == 1) pthread_cond_signal(&cond); g_Flag = 2; printf("this is thread2, g_Flag: %d, thread id is %u/n",g_Flag, (unsigned int)pthread_self()); pthread_mutex_unlock(&mutex); printf("leave thread2/n"); pthread_exit(0);}

編譯運行可以得到符合要求的結果!

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产在线播放不卡| 欧美激情小视频| 成人激情视频网| 秋霞av国产精品一区| 欧美精品久久久久久久久| 欧美成人免费一级人片100| 日韩免费电影在线观看| 国产精品视频内| 成人免费在线视频网站| 亚洲午夜精品久久久久久性色| 欧美专区福利在线| 亚洲自拍在线观看| 久久亚洲春色中文字幕| 久久久久久国产精品久久| 亚洲国产精品成人va在线观看| 国产一区二区三区视频免费| 懂色aⅴ精品一区二区三区蜜月| 亚洲bt天天射| 久久伊人精品一区二区三区| 久久国产视频网站| 中文字幕精品www乱入免费视频| 久久久人成影片一区二区三区| 国产精品入口日韩视频大尺度| 亚洲欧美综合另类中字| 美乳少妇欧美精品| 色偷偷av亚洲男人的天堂| 亚洲人成在线电影| 久久久精品2019中文字幕神马| 亚洲乱码av中文一区二区| 国产美女高潮久久白浆| 国内精品久久久久久| 久久国产精品久久久久久久久久| 日韩在线欧美在线国产在线| 麻豆一区二区在线观看| 成人乱人伦精品视频在线观看| 亚洲乱码国产乱码精品精天堂| 欧美日韩国产在线播放| 亚洲区一区二区| 成人性生交xxxxx网站| 久久国内精品一国内精品| 久久影院免费观看| 亚洲精品自拍第一页| 91亚洲精品视频| 日韩成人激情视频| 国产欧美在线观看| 日韩在线免费高清视频| 97在线视频免费| 亚洲人成在线观看网站高清| 国产精品草莓在线免费观看| 欧美丝袜美女中出在线| 午夜欧美不卡精品aaaaa| 欧美视频二区36p| 最近2019中文字幕第三页视频| 亚洲亚裔videos黑人hd| 亚洲国产小视频在线观看| 欧美日韩一二三四五区| 欧美日韩国产中文字幕| 91视频免费在线| 成人激情视频在线| 亚洲成人a**站| 欧美乱妇高清无乱码| 中文字幕亚洲第一| 91精品国产自产91精品| 国产精品专区h在线观看| 51色欧美片视频在线观看| 欧美乱人伦中文字幕在线| 精品毛片网大全| 亚洲精品一区二区久| 日本欧美精品在线| 国模视频一区二区| 亚洲电影av在线| 午夜精品一区二区三区视频免费看| 国产精品一区专区欧美日韩| 亚洲欧洲第一视频| 国产成人精品一区二区三区| 国产精品91一区| 2020欧美日韩在线视频| 国产精品第一页在线| 亚洲精品国精品久久99热| 欧美日韩免费看| 国产精品久久久久久久电影| 日韩av免费网站| 久久午夜a级毛片| 国产香蕉精品视频一区二区三区| 日韩在线欧美在线| 91精品久久久久久久久青青| 欧美成aaa人片在线观看蜜臀| 亚洲一区二区三区sesese| 欧美午夜电影在线| 91精品久久久久久久| 国产成+人+综合+亚洲欧美丁香花| 国产精品免费视频xxxx| 国产亚洲欧美日韩美女| 日韩国产中文字幕| 日韩第一页在线| 日韩电影第一页| 成人福利视频在线观看| 亚洲成人久久久久| 精品国产依人香蕉在线精品| 国产精品成人免费视频| 91欧美精品午夜性色福利在线| 91精品视频免费观看| 久久久久国产精品免费| 伦伦影院午夜日韩欧美限制| 国产精品久久久久999| 色樱桃影院亚洲精品影院| 国产精品99久久99久久久二8| 亚洲欧美国产一区二区三区| 亚洲人成电影网站色xx| 黑人欧美xxxx| 日日骚av一区| 欧美国产欧美亚洲国产日韩mv天天看完整| 一区二区亚洲欧洲国产日韩| 91精品一区二区| 国产97在线播放| 国产91ⅴ在线精品免费观看| 亚洲а∨天堂久久精品喷水| 亚洲国产精彩中文乱码av在线播放| 欧美午夜片欧美片在线观看| www.日韩av.com| 国产丝袜一区二区| 97在线看免费观看视频在线观看| 国产91久久婷婷一区二区| 成人国产在线视频| 2019亚洲男人天堂| 国产精品9999| 国产成人激情视频| 91精品国产电影| 日本精品视频在线| 亚洲最大成人免费视频| 成人国产亚洲精品a区天堂华泰| 久久伊人精品一区二区三区| 日韩中文在线不卡| 国产欧美精品在线播放| 国产91精品青草社区| 国产91精品黑色丝袜高跟鞋| 欧美日韩精品在线视频| 日韩高清免费在线| 91亚洲国产精品| 91在线观看免费观看| 久久久精品999| 日韩在线高清视频| 91精品国产九九九久久久亚洲| 日韩av大片免费看| 狠狠躁夜夜躁人人爽天天天天97| 国产91对白在线播放| 黄色成人在线免费| 91午夜在线播放| 亚洲欧洲美洲在线综合| 亚洲变态欧美另类捆绑| 亚洲美女喷白浆| 久久精品99久久久香蕉| 91国内在线视频| 国产综合视频在线观看| 中文字幕欧美专区| 国产做受高潮69| 欧美成人在线免费视频| 国产精品亚洲精品| 国产精品久久久久久久久久三级| 91亚洲永久免费精品| 97在线精品视频| 琪琪第一精品导航| 国产一区二区成人| 91热福利电影|