線程概述
程序是系統中程式執行和資源配置設定的基本機關。
每個程序都擁有自己的資料段、代碼段和堆棧段,這就造成了程序在進行切換等操作時都需要有比較負責的上下文切換等動作。為了進一步減少處理機的空轉時間支援多處理器和減少上下文切換開銷,程序在演化中出現了另一個概念——線程。它是一個程序内的基本排程機關,也可以稱為輕量級程序。
線程是在共享記憶體空間中并發的多道執行路徑,它們共享一個程序的資源,如檔案描述和信号處理。是以,大大減少了上下文切換的開銷。
同程序一樣,線程也将相關的變量值放線上程控制表内TCB。
一個程序可以有多個線程,也就是有多個線程控制表及堆棧寄存器,但卻共享一個使用者位址空間。
要注意的是,由于線程共享了程序的資源和位址空間,是以,任何線程對系統資源的操作都會給其他線程帶來影響,是以,多線程中的同步就是非常重要的問題了。
在多線程系統中,程序與線程的關系如表 8.1 所示。
Linux線程實作
1.線程建立和退出
(1)函數說明
線程建立:pthread_create。
線程退出:
(1)線上程建立以後,就開始運作相關的線程函數,在該函數運作完之後,該線程也就退出了,這也是線程退出一種方法。
(2)另一種退出線程的方法是使用函數 pthread_exit。由于 exit 的作用是使調用程序終止,往往一個程序包含多個線程,是以,在使用 exit 之後,該程序中的所有線程都終止了。是以,線上程中就可以使用 pthread_exit 來代替程序中的 exit。
線程退出之後,退出線程所占用的資源并不會随着線程的終止而得到釋放。
pthread_join()函數。pthread_join 可以用于将目前線程挂起,等待線程的結束。這個函數是一個線程阻塞的函數,調用它的函數将一直等待到被等待的線程結束為止,當函數傳回時,被等待線程的資源就被收回。
*如:pthread_create( &id3,NULL, (void )thread3,&tmp );
以下執行個體中建立了兩個線程,其中第一個線程是在程式運作到中途時調用 pthread_exit函數退出,第二個線程正常運作退出。在主線程中收集這兩個線程的退出資訊,并釋放資源。從這個執行個體中可以看出,這兩個線程是并發運作的。
gcc -o th th.c -lpthread (編譯的時候要加入線程庫)
由于線程共享程序的資源和位址空間,是以在對這些資源進行操作時,必須考慮到線程間資源通路的惟一性問題,這裡主要介紹 POSIX 中線程同步的方法,主要有互斥鎖和信号量的方式。
2.mutex 互斥鎖線程控制 (1)函數說明
mutex是一種簡單的加鎖的方法來控制對共享資源的存取。
這個互斥鎖隻有兩種狀态,也就是上鎖和解鎖.
可以把互斥鎖看作某種意義上的全局變量。在同一時刻隻能有一個線程掌握某個互斥上的鎖,擁有上鎖狀态的線程能夠對共享資源進行操作。若其他線程希望上鎖一個已經上鎖了的互斥鎖,則該線程就會挂起,直到上鎖的線程釋放掉互斥鎖為止。可以說,這把互斥鎖使得共享資源按序在各個線程中操作。
互斥鎖初始化:pthread_mutex_init
互斥鎖上鎖:pthread_mutex_lock—阻塞
互斥鎖判斷上鎖:pthread_mutex_trylock —非阻塞(如果已經上鎖,則傳回EBUSY;如果沒上鎖,則給它上鎖,成功為0,不成功為其他值)
互斥鎖解鎖:pthread_mutex_unlock
消除互斥鎖:pthread_mutex_destroy
互斥鎖可以分為:
快速鎖:指調用線程會阻塞直至擁有互斥鎖的線程解鎖為止。
遞歸互斥鎖:能夠成功地傳回并且增加調用線程在互斥上加鎖的次數.
檢錯互斥鎖:則為快速互斥鎖的非阻塞版本,它會立即傳回并傳回一個錯誤資訊。
3.信号量線程控制 (1)信号量說明
信号量也就是作業系統中所用到的 PV 原語,它廣泛用于程序或線程間的同步與互斥,它本質上是一個非負的整數計數器。
PV 原語是對整數計數器信号量 sem 的操作。一次 P 操作使 sem 減一(配置設定資源),而一次 V 操作使sem 加一(釋放資源)。
程序(或線程)根據信号量的值來判斷是否對公共資源具有通路權限。當信号量sem 的值大于或等于0時,該程序(或線程)具有公共資源的通路權限;相反,當信号量 sem的值小于零時,該程序(或線程)就将阻塞直到信号量 sem 的值大于等于0 為止。
PV原語主要用于程序或線程間的同步和互斥這兩種典型情況。若用于互斥,幾個程序(或線程)往往隻設定一個信号量 sem,它們的操作流程如圖 8.2 所示。
當信号量用于同步操作時,往往會設定多個信号量,并安排不同的初始值來實作它們之間的順序執行,它們的操作流程如圖 8.3 所示。
sem_init 用于建立一個信号量,并能初始化它的值。
sem_wait 和 sem_trywait 相當于 P 操作,它們都能将信号量的值減一,兩者的差別在于若信号量小于0時,sem_wait 将會阻塞程序,而 sem_trywait 則會立即傳回。
sem_post 相當于 V 操作,它将信号量的值加一同時發出信号喚醒等待的程序。
sem_getvalue 用于得到信号量的值。
sem_destroy 用于删除信号量。
信号量鎖例子:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <sys/ipc.h>
#include <semaphore.h>
int lock_var;
time_t end_time;
sem_t sem1,sem2,sem3;
void pthread1(void *arg);
void pthread2(void *arg);
void pthread3(void *arg);
int main(int argc, char *argv[])
{
pthread_t id1,id2,id3;
pthread_t mon_th_id;
int ret;
end_time = time(NULL)+30;
ret=sem_init(&sem1,0,1);
ret=sem_init(&sem2,0,0);
ret=sem_init(&sem3,0,0);
if(ret!=0)
{
perror("sem_init");
}
ret=pthread_create(&id1,NULL,(void *)pthread1, NULL);
if(ret!=0)
perror("pthread cread1");
ret=pthread_create(&id2,NULL,(void *)pthread2, NULL);
if(ret!=0)
perror("pthread cread2");
ret=pthread_create(&id3,NULL,(void *)pthread3, NULL);
if(ret!=0)
perror("pthread cread2");
pthread_join(id1,NULL);
pthread_join(id2,NULL);
pthread_join(id3,NULL);
exit(0);
}
void pthread1(void *arg)
{
int i;
while(time(NULL) < end_time){
sem_wait(&sem1);
for(i=0;i<2;i++){
sleep(1);
lock_var++;
printf("pthread1----lock_var=%d\n",lock_var);
}
printf("pthread1:lock_var=%d\n",lock_var);
sem_post(&sem2);
sleep(1);
}
}
void pthread2(void *arg)
{
int nolock=0;
int ret;
while(time(NULL) < end_time){
sem_wait(&sem2);
printf("pthread2:pthread2 got lock;lock_var=%d\n",lock_var);
sem_post(&sem3);
sleep(3);
}
}
void pthread3(void *arg)
{
int nolock=0;
int ret;
while(time(NULL) < end_time){
sem_wait(&sem3);
printf("pthread3:pthread3 got lock;lock_var=%d\n",lock_var);
sem_post(&sem1);
sleep(3);
}
}