線程是什麼
線程是進程內部的一條執行序列(執行流),每個進程至少有一條執行序列:main的執行體。進程可以通過線程庫創建N條線程,新建的線程爲函數線程,main是主線程。雖然感覺是進程包含着線程,進程由線程組成,但是進程提出的概念比線程概念要早。
線程與進程的區別
1.進程是資源分配的最小單位,線程是CPU調度的最小單位。
2.線程是輕量級的進程
3.管理方式不一樣。進程是pcb管理,線程是由線程結構管理。
線程的實現的三種方式
1、用戶級 n:1關係
特點:用戶管理複雜,內核管理簡單
2、內核級 n:n
用戶管理簡單,內核管理複雜,cpu頻繁中斷,效率低
3、混合模式 n:m
我們現在使用的操作系統大部分爲混合模式,內核級和用戶級的優點和缺點都涵蓋了,但是總體來說還是利大於弊。
線程的創建:
Int pthread_create(pthread_t *id,pthread_attr_t *attr,void* (*fun)(void *) ,void *arg);
功能:創建出一個函數線程。(編譯時要鏈接庫 gcc -o *** ***.c -lpthread)
第一個參數爲標識符的地址。
第二個參數用來設置線程的屬性,目前先寫NULL就好
第三個參數爲線程函數的地址,這個函數的返回值和參數都是void*類型的
第四個參數爲第三個參數指針指向的函數的參數。這裏的傳參有兩種方式,(都用整形的a來舉例)
(1)將值強轉成void*(不適用於float /double /struct /char *....)
參數裏(void*)a 函數裏int a=(int )arg;
2、將地址強轉成void* (此種更適用多種情況)
參數裏(void *)&a 函數裏Int a=*(int *)arg;
注意:
創建出來的函數線程不同於函數調用,函數調用是這條執行流的一部分。
函數線程是創建出一條獨立的執行序列,他與主線程同時執行。
int pthread_exit(void *);
功能:線程結束
參數可以設置線程結束狀態,退出信息。
int pthread_join(pthread_t id,void **);
功能:等待線程結束
可以獲取到等待的線程通過pthread_exit 設置的狀態信息。
這三個函數我們用以下一系列的例子來說明它們的用處和用法
比如一個進程裏main線程每秒打印一次main running 共 5次
創建的函數線程每秒打印一次pthread running 共 3次
結果貌似很正常。
但如果把兩個線程的打印次數互換呢?即main3次,創建的線程5次。
我們可以看到創建的線程並沒有完成工作,就隨着main線程的結束而結束了。
這是因爲main線程結束時,會默認的使用exit(0)退出整個進程,因此其他線程就因此也結束了。
那麼怎麼才能防止main線程退出整個進程呢?那就該用到 pthread_exit退出線程函數了,main最後使用這個退出線程,就不會再使用exit(0)了,其他線程也可以執行完畢。這裏再強調一個使用習慣,就是每個線程的最後都最好加上這個退出線程函數。
這樣pthread線程就打印了5次了。
可是如果不想讓main線程結束呢?又想讓pthread線程完成它的工作。那麼就要用到等待函數了。
在main線程執行完3次打印後,暫停運行 等待pthread線程執行完畢後,main線程繼續運行。
最後爲了證明main線程還活着,加了句printf。
可以看到main線程仍在,pthread也完成了5次打印。
那麼等待函數的那個參數 void**究竟是幹什麼用的呢?其實它和 退出函數的參數有關係,退出函數的參數不是一個void *嗎?其實這個void **就是來記錄 要等待的那個線程的退出時的一些信息(就是退出函數裏的參數)。比如pthread線程執行完之後要使用退出函數,使用 pthread_exit("i finish my work!")裏面那個字符串就是線程退出時想表達的信息,意思“我完成了我的工作”。有時線程可能不一定完成任務。我們會在條件判斷一下,然後再邏輯判斷的分支後使用退出函數 傳入特定的出錯參數,到時候main線程或者其他發起等待的線程就能通過這個參數得知這個子線程究竟完成任務與否,在決定如何進行下一步的。