C語言中平行的世界——線程
假如你的程序一邊從網絡讀取數據,一邊向網絡發送數據。如何在代碼中同時執行幾個不同的任務?根據前面的知識,我們知道可以通過創建多幾個子進程來做這些事。但是創建進程很花時間,而且不同子進程之間共享數據很不方便。這時我們需要線程來幫我們解決這個問題。
如何創建線程呢?
我們有很多線程庫可以用,其中最流行的就是POSIX線程庫,也叫pthread 。可以在類Unix系統上使用。
假設我想在獨立的線程中運行以下這兩個函數:
void one(void *a){ int i = 0; for(i = 0;i<5;i++){ sleep(1); puts("one one one"); } return NULL; } void two(void *b){ int i = 0; for(i = 0;i < 5; i++){ sleep(1); puts("two two two"); } return NULL: }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
注意:線程函數的返回類型必須是void,void指針可以指向存儲器中任何類型的數據。*
用pthread_create創建線程
我們將創建兩個線程,每個線程都需要把信息保存在一個叫pthread_t的結構體中,然后就可以用pthread_create()創建并運行線程。
pthread_t t0;//保存了線程的所有信息 pthread_t t1;//保存了線程的所有信息 if(pthread_create(&t0,NULL,one,NULL) == -1){//創建線程 error1("無法創建線程t0"); } if(pthread_create(&t1,NULL,two,NULL) == -1){//創建線程 error1("無法創建線程t1"); }
1
2
3
4
5
6
7
8
代碼將以獨立線程運行這兩個函數。如果程序運行完這段代碼就結束了,線程也會隨之滅亡,因此程序必須等待線程結束。加上以下的代碼,就可以讓程序等線程執行完成,再結束。否則線程還沒來得及執行,程序就結束了。
void* result; //函數返回 的void指針會保存在這里 if(pthread_join(t0,&result) == -1){ error1("無法收回線程t0"); } if(pthread_join(t1,&result) == -1){ error1("無法收回線程t1"); }
1
2
3
4
5
6
7
pthread_join()函數會等待線程結束并接收線程函數的返回值,并把它保存在一個void指針變量中。一旦兩個線程都結束了,程序就可以順利退出。
編譯時要使用 pthread庫,所以必須在編譯程序時鏈接它:
~/Desktop/MyC$ gcc testThread.c -lpthread -o thread
1
運行程序:
~/Desktop/MyC$ ./thread one one one two two two two two two one one one two two two one one one two two two one one one two two two one one one
1
2
3
4
5
6
7
8
9
10
11
線程不安全
線程最大的優點就是多個不同任務可以同時運行,并訪問相同數據,而不同任務可以訪問相同數據恰恰也是線程的缺點。怎么辦呢?我們可以通過互斥鎖來防止不同線程同時訪問共享資源,即不同線程不能同時讀取相同數據,并把它寫回。
為了保護某段代碼的安全,我們需要創建互斥鎖:
pthread_mutex_t a_lock = PTHREAD_MUTEX_INITIALIZER;
1
互斥鎖必須對所有可能發生沖突的線程可見,因此它必須是一個全局變量 。PTHREAD_MUTEX_INITIALIZER實際上是一個宏,當編譯器看到它時,就會插入創建互斥鎖的代碼。
pthread_mutex_lock()函數只允許一個線程通過,其他線程運行到這行代碼時必須等待。當線程到達代碼的尾部就要調用pthread_mutex_unlock()釋放互斥鎖。
pthread_mutex_lock(&a_lock);//含有共享數據的代碼從這里開始 .... //含有共享數據的代碼 pthread_mutex_unlock(&a_lock);//釋放互斥鎖,其他線程就可以入這段含有共享數據的代碼。
1
2
3
我們給出一個完整示例testThread.c:
#include
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
編譯運行:
~/Desktop/MyC$ gcc testThread.c -lpthread -o thread ~/Desktop/MyC$ ./thread Window No.0 TICKETS:398 Window No.1 TICKETS:300 Window No.2 TICKETS:200 Window No.3 TICKETS:100 Window No.4 TICKETS:0
1
2
3
4
5
6
7
線程函數可以接收一個void指針作為參數,并返回一個void指針值,通常你希望把某個整型值傳給線程,并讓它返回某個整型值,一種方法是用long,因為它的大小和void指針相同 ,可以把它保存在void指針變量中,如下面程序thread1.c:
#include
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
編譯運行:
~/Desktop/MyC$ gcc thread1.c -lpthread -o thread1 ~/Desktop/MyC$ ./thread1 Thread number 0 Thread number 1 Thread number 2 Thread 0 returned 1 Thread 1 returned 2 Thread 2 returned 3
1
2
3
4
5
6
7
8
操作系統怎樣運行多個線程呢?其實操作系統會在多個線程之間快速切換,看起來就好像在同時做多件事。
線程不一定能讓程序更快,雖然線程可以幫助你利用機器上更多的處理器和核,但你還是需要控制代碼中互斥鎖的數量,用得太多互斥鎖,代碼可能會像單線程程序一樣慢。
怎樣才能讓多線程程序運行得更快?首先要減少線程需要訪問的共享數據的數量。如果線程無需訪問很多共享數據,那么多個線程等一個線程的情況就會很少出現,速度就會大大提高。
線程通常比進程更快,因為創建進程要比創建線程花更多時間。
死鎖是如何發生的?假設你有兩個線程,它們都想得到互斥鎖A和B,如果第一個線程得到了A,第二個線程得到了B,這兩個線程就會陷入死鎖。因為第一個線程無法得到B,第二個線程無法得到A,它們都停滯不前。
謝謝閱讀!
C 語言 任務調度
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。