新聞中心
隨著計(jì)算機(jī)技術(shù)的飛速發(fā)展,人們對(duì)計(jì)算機(jī)性能的要求也越來越高。為了提高程序的執(zhí)行效率,多線程編程成為了一個(gè)不可或缺的手段。而作為高效、穩(wěn)定性極佳的操作系統(tǒng)之一,Linux自然也成為了多線程編程的首選平臺(tái)。

十年的郫都網(wǎng)站建設(shè)經(jīng)驗(yàn),針對(duì)設(shè)計(jì)、前端、開發(fā)、售后、文案、推廣等六對(duì)一服務(wù),響應(yīng)快,48小時(shí)及時(shí)工作處理。成都營銷網(wǎng)站建設(shè)的優(yōu)勢(shì)是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動(dòng)調(diào)整郫都建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計(jì),從而大程度地提升瀏覽體驗(yàn)。創(chuàng)新互聯(lián)建站從事“郫都網(wǎng)站設(shè)計(jì)”,“郫都網(wǎng)站推廣”以來,每個(gè)客戶項(xiàng)目都認(rèn)真落實(shí)執(zhí)行。
本文將介紹Linux多線程編程的基本思想、相關(guān)概念及實(shí)例,幫助讀者掌握Linux多線程編程的核心技巧,提高程序性能,實(shí)現(xiàn)更為高效的計(jì)算機(jī)應(yīng)用程序。
一、多線程編程的基本思想
多線程編程的基本思想是利用多線程來解決單線程執(zhí)行效率低下的問題。多線程程序在執(zhí)行時(shí)會(huì)分配多個(gè)線程來同時(shí)執(zhí)行不同的任務(wù),從而加速整個(gè)程序的執(zhí)行速度。
多線程編程的核心思想是并發(fā)執(zhí)行。在單線程程序中,代碼是按照一定的順序執(zhí)行的,需要等到當(dāng)前任務(wù)完成之后,才能進(jìn)行下一個(gè)任務(wù)。而在多線程程序中,每個(gè)線程可以并發(fā)執(zhí)行一個(gè)任務(wù),不需要等待其他線程完成,從而更大化地利用了處理器的并行性能。
在 Linux 中,多線程編程程序是采用線程庫來實(shí)現(xiàn)的。線程是相對(duì)輕量級(jí)的執(zhí)行單元,線程的創(chuàng)建、管理和銷毀都是由操作系統(tǒng)負(fù)責(zé)的,使開發(fā)人員能夠針對(duì)問題的性質(zhì)選擇合適的并發(fā)模型,通過線程庫對(duì)線程進(jìn)行管理和調(diào)度,實(shí)現(xiàn)高效的程序執(zhí)行。
二、多線程編程的相關(guān)概念
1.線程和進(jìn)程
在 Linux 中,一個(gè)進(jìn)程可以包含多個(gè)線程。進(jìn)程是操作系統(tǒng)中任務(wù)的基本單位,而線程則是進(jìn)程中的執(zhí)行單元。每個(gè)線程擁有自己的棧、程序計(jì)數(shù)器和寄存器,以及可自行調(diào)度的優(yōu)先級(jí)等信息,但不擁有獨(dú)立的代碼、數(shù)據(jù)和堆??臻g。
多線程程序中,通常會(huì)同時(shí)創(chuàng)建多個(gè)線程,每個(gè)線程都可以獨(dú)立進(jìn)行執(zhí)行不同的任務(wù)。線程之間可以共享進(jìn)程的資源,如內(nèi)存空間、文件描述符等,但也需要考慮線程競爭和同步問題。
2.線程同步和互斥
在線程中,如果多個(gè)線程同時(shí)訪問同一塊資源(如內(nèi)存),就可能會(huì)發(fā)生不可預(yù)期的情況,如數(shù)據(jù)混雜、死鎖等。因此,在多線程編程中必須采用互斥和同步技術(shù)(如信號(hào)量、互斥鎖、條件變量等)來解決線程之間的競爭問題,保證線程執(zhí)行的正確性。
互斥是一種保證臨界區(qū)內(nèi)代碼不被多個(gè)線程同時(shí)執(zhí)行的方法。在臨界區(qū)的代碼中,需要加入互斥鎖進(jìn)行保護(hù),當(dāng)一個(gè)線程進(jìn)入臨界區(qū)時(shí),會(huì)鎖定互斥鎖,其他線程則不能進(jìn)入臨界區(qū),直到當(dāng)前線程解鎖互斥鎖,其他線程才能繼續(xù)執(zhí)行臨界區(qū)的代碼。
同步則是通過信號(hào)量、條件變量等來實(shí)現(xiàn)線程之間的協(xié)作。例如,生產(chǎn)者線程和消費(fèi)者線程之間,需要通過信號(hào)量來協(xié)調(diào)生產(chǎn)和消費(fèi)的數(shù)量和時(shí)間,避免隊(duì)列溢出和資源浪費(fèi)等問題。
三、多線程編程的實(shí)例
下面通過代碼示例來說明Linux下多線程編程的實(shí)現(xiàn)過程。
1、創(chuàng)建線程
在 Linux 中,線程的創(chuàng)建可以采用 pthread_create() 函數(shù)實(shí)現(xiàn)。函數(shù)原型如下:
#include
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
其中,參數(shù)說明如下:
thread:指向 pthread_t 類型數(shù)據(jù)的指針,用于存儲(chǔ)創(chuàng)建后線程的 ID;
attr:線程屬性,通常為 NULL;
start_routine:指向線程主函數(shù)的指針;
arg:向線程主函數(shù)傳遞的參數(shù)。
2、線程同步和互斥
在多線程編程中,需要考慮線程的同步和互斥,以保證程序的正確性和穩(wěn)定性。例如,下面實(shí)現(xiàn)了一個(gè)線程安全的隊(duì)列:
typedef struct {
void **items;
int max_size;
int front;
int rear;
pthread_mutex_t lock;
pthread_cond_t has_items;
pthread_cond_t has_space;
} SafeQueue;
void safe_queue_init(SafeQueue *queue, int max_items) {
queue->items = malloc(sizeof(void*) * max_items);
queue->max_size = max_items;
queue->front = 0;
queue->rear = 0;
pthread_mutex_init(&queue->lock, NULL);
pthread_cond_init(&queue->has_items, NULL);
pthread_cond_init(&queue->has_space, NULL);
}
void safe_queue_push(SafeQueue *queue, void *item) {
pthread_mutex_lock(&queue->lock);
while(queue->rear >= queue->max_size) {
pthread_cond_wt(&queue->has_space, &queue->lock);
}
queue->items[queue->rear] = item;
queue->rear++;
pthread_cond_signal(&queue->has_items);
pthread_mutex_unlock(&queue->lock);
}
void *safe_queue_pop(SafeQueue *queue) {
pthread_mutex_lock(&queue->lock);
while(queue->front == queue->rear) {
pthread_cond_wt(&queue->has_items, &queue->lock);
}
void *item = queue->items[queue->front];
queue->front++;
pthread_cond_signal(&queue->has_space);
pthread_mutex_unlock(&queue->lock);
return item;
}
在上述代碼中,通過互斥鎖 pthread_mutex_lock() 和信號(hào)量 pthread_cond_wt() 來保證線程的安全同步,滿足生產(chǎn)者和消費(fèi)者之間的交替需求;同時(shí),還使用了條件變量 pthread_cond_signal() 來及時(shí)通知對(duì)應(yīng)線程,避免線程出現(xiàn)阻塞等待的情況。
3、線程池
在多線程編程中,線程池是一種非常重要的技術(shù),可以有效地控制線程的資源使用,并提高程序的效率。下面實(shí)現(xiàn)了一個(gè)線程池的例子:
typedef struct {
SafeQueue task_queue;
pthread_t *threads;
int num_threads;
int shutdown;
} ThreadPool;
void thread_pool_init(ThreadPool *pool, int num_threads) {
pool->threads = malloc(sizeof(pthread_t) * num_threads);
pool->num_threads = num_threads;
pool->shutdown = 0;
safe_queue_init(&pool->task_queue, num_threads);
for(int i = 0; i
pthread_create(&pool->threads[i], NULL, thread_pool_worker, pool);
}
}
void thread_pool_add_task(ThreadPool *pool, void (*task)(void *), void *arg) {
Task *t = malloc(sizeof(Task));
t->task_fn = task;
t->arg = arg;
safe_queue_push(&pool->task_queue, t);
}
void thread_pool_shutdown(ThreadPool *pool) {
for(int i = 0; i num_threads; i++) {
pthread_join(pool->threads[i], NULL);
}
pool->shutdown = 1;
}
void *thread_pool_worker(void *arg) {
ThreadPool *pool = (ThreadPool*) arg;
while(!pool->shutdown) {
Task *t = safe_queue_pop(&pool->task_queue);
if(t != NULL) {
t->task_fn(t->arg);
free(t);
}
}
return NULL;
}
在上述代碼中,我們首先通過 SafeQueue 實(shí)現(xiàn)了一個(gè)線程安全的任務(wù)隊(duì)列,然后通過線程池實(shí)現(xiàn)了任務(wù)的分發(fā)和管理。線程池包含了多個(gè)線程,每個(gè)線程都會(huì)不斷地從隊(duì)列中獲取任務(wù)進(jìn)行處理,每個(gè)任務(wù)都將被一個(gè)獨(dú)立的線程執(zhí)行,從而有效地發(fā)揮了計(jì)算機(jī)的并行性能。
成都網(wǎng)站建設(shè)公司-創(chuàng)新互聯(lián)為您提供網(wǎng)站建設(shè)、網(wǎng)站制作、網(wǎng)頁設(shè)計(jì)及定制高端網(wǎng)站建設(shè)服務(wù)!
linux 多線程環(huán)境下的幾種鎖機(jī)制
NO1
互斥量(Mutex)
互斥量是實(shí)現(xiàn)最簡單的鎖類型,因此有一些教科書一般以互斥量為例對(duì)鎖原語進(jìn)行描述?;コ饬康尼尫挪⒉粌H僅依賴于釋放操作,還可以引入一個(gè)定時(shí)器屬性。如果在釋放操作執(zhí)行前發(fā)生定時(shí)器超時(shí),則互斥量也會(huì)釋放代碼塊或共享存儲(chǔ)區(qū)供其他線程訪問。當(dāng)有異常發(fā)生時(shí),可使用try-finally語句來確?;コ饬勘会尫?。定時(shí)器狀態(tài)或try-finally語句的使用可以避免產(chǎn)生死鎖。
遞歸鎖(Recursive
Lock)
遞歸鎖是指可以被當(dāng)前持有該鎖的線程重復(fù)獲取,而不會(huì)導(dǎo)致該線程產(chǎn)生死鎖的鎖類型。對(duì)遞歸鎖而言,只有在當(dāng)前持有線程的獲取鎖操作都有一個(gè)釋放操作與之對(duì)應(yīng)時(shí),其他線程才可以獲取該鎖。因此,在使用遞歸鎖時(shí),必須要用足夠的釋放鎖操作來平衡獲取鎖操作,實(shí)現(xiàn)這一目標(biāo)的更佳方式是在單入口單出口代碼塊的兩頭一一對(duì)應(yīng)地使用獲取、釋放操作,做法和在普通鎖中一樣。遞歸鎖在遞歸函數(shù)中最有用。但是,總的來說,遞歸鎖比非遞歸鎖速度要慢。需要注意的是:調(diào)用線程獲得幾次遞歸鎖必須釋放幾次遞歸鎖。
以下為一個(gè)遞歸鎖的示例:
view plain copy
Recursive_Lock L
void recursiveFunction (int count) {
L->acquire()
if (count > 0) {
count = count – 1;
recursiveFunction(count);
}
L->release();
}
讀寫鎖(Read-Write
lock) 讀寫鎖又稱為共享獨(dú)占鎖(shared-exclusive
lock)、多讀單寫鎖(multiple-read/single-write lock)或者非互斥信號(hào)量(non-mutual
exclusion
semaphore)。讀寫鎖允許多個(gè)線程同時(shí)進(jìn)行讀訪問,但是在某一時(shí)刻卻最多只能由一個(gè)線程執(zhí)行寫操作。對(duì)于多個(gè)線程需要同時(shí)讀共享數(shù)據(jù)卻并不一定進(jìn)行寫操作的應(yīng)用來說,讀寫鎖是一種高效的同步機(jī)制。對(duì)于較長的共享數(shù)據(jù),只為其設(shè)置一個(gè)讀寫鎖會(huì)導(dǎo)致較長的訪問時(shí)間,更好將其劃分為多個(gè)小段并設(shè)置多個(gè)讀寫鎖以進(jìn)行同步。
這個(gè)讀寫鎖我們?cè)趯W(xué)習(xí)數(shù)據(jù)庫的時(shí)候應(yīng)該很熟悉的喲!
旋轉(zhuǎn)鎖(Spin
Lock)
旋轉(zhuǎn)鎖是一種非阻塞鎖,由某個(gè)線程獨(dú)占。采用旋轉(zhuǎn)鎖時(shí),等待線程并不靜態(tài)地阻塞在同步點(diǎn),而是必須“旋轉(zhuǎn)”,不斷嘗試直到最終獲得該鎖。旋轉(zhuǎn)鎖多用于多處理器系統(tǒng)中。這是因?yàn)?,如果在單核處理器中采用旋轉(zhuǎn)鎖,當(dāng)一個(gè)線程正在“旋轉(zhuǎn)”時(shí),將沒有執(zhí)行資源可供另一釋放鎖的線程使用。旋轉(zhuǎn)鎖適合于任何鎖持有時(shí)間少于將一個(gè)線程阻塞和喚醒所需時(shí)間的場合。線程控制的變更,包括線程上下文的切換和線程數(shù)據(jù)結(jié)構(gòu)的更新,可能比旋轉(zhuǎn)鎖需要更多的指令周期。旋轉(zhuǎn)鎖的持有時(shí)間應(yīng)該限制在線程上下文切換時(shí)間的50%到100%之間(Kleiman,1996年)。在線程調(diào)用其他子系統(tǒng)時(shí),線程不應(yīng)持有旋轉(zhuǎn)鎖。對(duì)旋轉(zhuǎn)鎖的不當(dāng)使用可能會(huì)導(dǎo)致線程餓死,因此需謹(jǐn)慎使用這種鎖機(jī)制。旋轉(zhuǎn)鎖導(dǎo)致的餓死問題可使用排隊(duì)技術(shù)來解決,即每個(gè)等待線程按照先進(jìn)先出的順序或者隊(duì)列結(jié)構(gòu)在一個(gè)獨(dú)立的局部標(biāo)識(shí)上進(jìn)行旋轉(zhuǎn)。
淺談linux 多線程編程和 windows 多線程編程的異同
很早以前就想寫寫linux下多線程編程和windows下的多線程編程了,但是每當(dāng)寫時(shí)又不知道從哪個(gè)地方寫起,怎樣把自己知道的東西都寫出來,下面我就談世罩讓談linux多線程及線程同步,并將它和windows的多線程進(jìn)行比較,看看他們之間有什么相同點(diǎn)和不同的地方。
其實(shí)最開始我是搞windows下編程的,包括windows編程,windows 驅(qū)動(dòng),包括u驅(qū)動(dòng),ndis驅(qū)動(dòng),pci驅(qū)動(dòng),1394驅(qū)動(dòng)等等,同時(shí)也一條龍服務(wù),做windows下的應(yīng)用程序開發(fā),后面慢慢的我又對(duì)linux開發(fā)產(chǎn)生比較深的興趣和愛好,就轉(zhuǎn)到搞linux開發(fā)了。在接下來的我還會(huì)寫一些博客,主要是寫linux編程和windows編程的區(qū)別吧,現(xiàn)在想寫的是linux下u驅(qū)動(dòng)和windows下u驅(qū)動(dòng)開發(fā)的區(qū)別,這些都是后話,等我將linux多線程和windows多線程講解完后,我再寫一篇u驅(qū)動(dòng),談?wù)剋indows 和linux u驅(qū)動(dòng)的東東。好了,言歸正傳。開始將多線程了。
首先我們講講為什么搜局要采用多線程編程,其實(shí)并不是所有的程序都必須采用多線程,有些時(shí)候采用多線程,性能還沒有單線程好。所以我們要搞清楚,什么時(shí)候采用多線程。采用多線程的好處如下:
(1)因?yàn)槎嗑€程彼此之間采用相同的地址空間,共享大部分的數(shù)據(jù),這樣和多進(jìn)程相比,代價(jià)比較節(jié)儉,因?yàn)槎噙M(jìn)程的話,啟動(dòng)新的進(jìn)程必須分配給它獨(dú)立的地址空間,這樣需要數(shù)據(jù)表來維護(hù)代碼段,數(shù)據(jù)段和堆棧段等等。
(2)多線程和多進(jìn)程相比,一個(gè)明顯的優(yōu)點(diǎn)就是線程之間的通信了,對(duì)不同進(jìn)程來說,它們具有獨(dú)立的數(shù)據(jù)空間,要進(jìn)行數(shù)據(jù)的傳遞只能通過通信的方式進(jìn)行,這種方式不僅費(fèi)時(shí),而且很不方便。但是對(duì)于多線程就不一樣了。他們之間可以直接共享數(shù)據(jù),比如最簡單的方式就是共享全局變量。但是共享全部變量也要注意哦,呵呵,必須注意同步,不然后果你知道的。呵呵。
(3)在多cpu的情況下,不同的線程可以運(yùn)行不同的cpu下,這樣就完全并行了。
反正我覺得在這種情況下,采用多線程比較理想。比如說你要做一個(gè)任務(wù)分2個(gè)步驟,你為提高工作效率,你可以多線程技術(shù),開辟2個(gè)線程,之一個(gè)線程就做之一步的工作,第2個(gè)線程就做第2步的工作。但是你這個(gè)時(shí)候要注意同步了。因?yàn)橹挥兄徊阶鐾瓴拍茏龅?步的工作。這時(shí),我們可以采用同步技術(shù)進(jìn)行線程之間的通信。
針對(duì)這種情況,我們首先講講多線程之間的通信,在windows平臺(tái)下,多線程之間通信采用的方法主要有:
(1)共享全局變量,這種悶仔方法是最容易想到的,呵呵,那就首先講講吧,比如說吧,上面的問題,之一步要向第2步傳遞收據(jù),我們可以之間共享全局變量,讓兩個(gè)線程之間傳遞數(shù)據(jù),這時(shí)主要考慮的就是同步了,因?yàn)槟愫竺娴木€程在對(duì)數(shù)據(jù)進(jìn)行操作的時(shí)候,你之一個(gè)線程又改變了數(shù)據(jù)的內(nèi)容,你不同步保護(hù),后果很嚴(yán)重的。你也知道,這種情況就是讀臟數(shù)據(jù)了。在這種情況下,我們最容易想到的同步方法就是設(shè)置一個(gè)bool flag了,比如說在第2個(gè)線程還沒有用完數(shù)據(jù)前,之一個(gè)線程不能寫入。有時(shí)在2個(gè)線程所需的時(shí)間不相同的時(shí)候,怎樣達(dá)到更大效率的同步,就比較麻煩了。咱們可以多開幾個(gè)緩沖區(qū)進(jìn)行操作。就像生產(chǎn)者消費(fèi)者一樣了。如果是2個(gè)線程一直在跑的,由于時(shí)間不一致,緩沖區(qū)遲早會(huì)溢出的。在這種情況下就要考慮了,是不讓數(shù)據(jù)寫入還是讓數(shù)據(jù)覆蓋掉老的數(shù)據(jù),這時(shí)候就要具體問題具體分析了。就此打住,呵呵。就是用bool變量控制同步,linux 和windows是一樣的。
既然講道了這里,就再講講其它同步的方法。同樣 針對(duì)上面的這個(gè)問題,共享全局變量同步問題。除了采用bool變量外,最容易想到的方法就是互斥量了。呵呵,也就是傳說中的加鎖了。windows下加鎖和linux下加鎖是類似的。采用互斥量進(jìn)行同步,要想進(jìn)入那段代碼,就先必須獲得互斥量。
linux上互斥量的函數(shù)是:
windows下互斥量的函數(shù)有:createmutex 創(chuàng)建一個(gè)互斥量,然后就是獲得互斥量waitforsingleobject函數(shù),用完了就釋放互斥量ReleaseMutex(hMutex),當(dāng)減到0的時(shí)候 內(nèi)核會(huì)才會(huì)釋放其對(duì)象。下面是windows下與互斥的幾個(gè)函數(shù)原型。
HANDLE WINAPI CreateMutex(
__in LPSECURITY_ATTRIBUTES lpMutexAttributes,
__in BOOL bInitialOwner,
__in LPCTSTR lpName
);
可以可用來創(chuàng)建一個(gè)有名或無名的互斥量對(duì)象
之一參數(shù) 可以指向一個(gè)結(jié)構(gòu)體SECURITY_ATTRIBUTES 一般可以設(shè)為null;
第二參數(shù) 指當(dāng)時(shí)的函數(shù)是不是感應(yīng)感應(yīng)狀態(tài) FALSE為當(dāng)前擁有者不會(huì)創(chuàng)建互斥
第三參數(shù) 指明是否是有名的互斥對(duì)象 如果是無名 用null就好。
DWORD WINAPI WaitForSingleObject(
__in HANDLE hHandle,
__in DWORD dwMilliseconds
);
之一個(gè)是 創(chuàng)建的互斥對(duì)象的句柄。第二個(gè)是 表示將在多少時(shí)間之后返回 如果設(shè)為宏INFINITE 則不會(huì)返回 直到用戶自己定義返回。
對(duì)于linux操作系統(tǒng),互斥也是類似的,只是函數(shù)不同罷了。在linux下,和互斥相關(guān)的幾個(gè)函數(shù)也要閃亮登場了。
pthread_mutex_init函數(shù):初始化一個(gè)互斥鎖;
pthread_mutex_destroy函數(shù):注銷一個(gè)互斥鎖;
pthread_mutex_lock函數(shù):加鎖,如果不成功,阻塞等待;
pthread_mutex_unlock函數(shù):解鎖;
pthread_mutex_trylock函數(shù):測(cè)試加鎖,如果不成功就立即返回,錯(cuò)誤碼為EBUSY;
至于這些函數(shù)的用法,google上一搜,就出來了,呵呵,在這里不多講了。windows下還有一個(gè)可以用來保護(hù)數(shù)據(jù)的方法,也是線程同步的方式
就是臨界區(qū)了。臨界區(qū)和互斥類似。它們之間的區(qū)別是,臨界區(qū)速度快,但是它只能用來同步同一個(gè)進(jìn)程內(nèi)的多個(gè)線程。臨界區(qū)的獲取和釋放函數(shù)如下:
EnterCriticalSection() 進(jìn)入臨界區(qū); LeaveCriticalSection()離開臨界區(qū)。 對(duì)于多線程共享內(nèi)存的東東就講到這里了。
(2)采用消息機(jī)制進(jìn)行多線程通信和同步,windows下面的的消息機(jī)制的函數(shù)用的多的就是postmessage了。Linux下的消息機(jī)制,我用的較少,就不在這里說了,如果誰熟悉的,也告訴我,呵呵。
(3)windows下的另外一種線程通信方法就是事件和信號(hào)量了。同樣針對(duì)我開始舉得例子,2個(gè)線程同步,他們之間傳遞信息,可以采用事件(Event)或信號(hào)量(Semaphore),比如之一個(gè)線程完成生產(chǎn)的數(shù)據(jù)后,就必須告訴第2個(gè)線程,他已經(jīng)把數(shù)據(jù)準(zhǔn)備好了,你可以來取走了。第2個(gè)線程就把數(shù)據(jù)取走。呵呵,這里可以采用消息機(jī)制,當(dāng)之一個(gè)線程準(zhǔn)備好數(shù)據(jù)后,就直接postmessage給第2個(gè)線程,按理說采用postmessage一個(gè)線程就可以搞定這個(gè)問題了。呵呵,不是重點(diǎn),省略不講了。
對(duì)于linux,也有類似的方法,就是條件變量了,呵呵,這里windows和linux就有不同了。要特別講講才行。
對(duì)于windows,采用事件和信號(hào)量同步時(shí)候,都會(huì)使用waitforsingleobject進(jìn)行等待的,這個(gè)函數(shù)的之一個(gè)參數(shù)是一個(gè)句柄,在這里可以是Event句柄,或Semaphore句柄,第2個(gè)參數(shù)就是等待的延遲,最終等多久,單位是ms,如果這個(gè)參數(shù)為INFINITE,那么就是無限等待了。釋放信號(hào)量的函數(shù)為ReleaseSemaphore();釋放事件的函數(shù)為SetEvent。當(dāng)然使用這些東西都要初始化的。這里就不講了。Msdn一搜,神馬都出來了,呵呵。神馬都是浮云!
對(duì)于linux操作系統(tǒng),是采用條件變量來實(shí)現(xiàn)類似的功能的。Linux的條件變量一般都是和互斥鎖一起使用的,主要的函數(shù)有:
pthread_mutex_lock ,
pthread_mutex_unlock,
pthread_cond_init
pthread_cond_signal
pthread_cond_wait
pthread_cond_timewait
為了和windows操作系統(tǒng)進(jìn)行對(duì)比,我用以下表格進(jìn)行比較:
對(duì)照以上表格,總結(jié)如下:
(1) Pthread_cleanup_push,Pthread_cleanup_pop:
這一對(duì)函數(shù)push和pop的作用是當(dāng)出現(xiàn)異常退出時(shí),做一些清除操作,即當(dāng)在push和pop函數(shù)之間異常退出,包括調(diào)用pthread_exit退出,都會(huì)執(zhí)行push里面的清除函數(shù),如果有多個(gè)push,注意是是棧,先執(zhí)行后面的那個(gè)函數(shù),在執(zhí)行前面的函數(shù),但是注意當(dāng)在這2個(gè)函數(shù)之間通過return 退出的話,執(zhí)不執(zhí)行push后的函數(shù)就看pop函數(shù)中的參數(shù)是不是為0了。還有當(dāng)沒有異常退出時(shí),等同于在這里面return退出的情況,即:當(dāng)pop函數(shù)參數(shù)不為0時(shí),執(zhí)行清除操作,當(dāng)pop函數(shù)參數(shù)為0時(shí),不執(zhí)行push函數(shù)中的清除函數(shù)。
(2)linux的pthread_cond_signal和SetEvent的不同點(diǎn)
Pthread_cond_singal釋放信號(hào)后,當(dāng)沒有Pthread_cond_wait,信號(hào)馬上復(fù)位了,這點(diǎn)和SetEvent不同,SetEvent是不會(huì)復(fù)位的。詳解如下:
條件變量的置位和復(fù)位有2種常用模型:之一種模型是當(dāng)條件變量置位時(shí)(signaled)以后,如果當(dāng)前沒有線程在等待,其狀態(tài)會(huì)保持為置位(signaled),直到有等待的線程進(jìn)入被觸發(fā),其狀態(tài)才會(huì)變?yōu)閡nsignaled,這種模型以采用Windows平臺(tái)上的Auto-set Event 為代表。
第2種模型則是Linux平臺(tái)的pthread所采用的模型,當(dāng)條件變量置位(signaled)以后,即使當(dāng)前沒有任何線程在等待,其狀態(tài)也會(huì)恢復(fù)為復(fù)位(unsignaled)狀態(tài)。
條件變量在Linux平臺(tái)上的這種模型很難說好壞,在實(shí)際應(yīng)用中,我們可以對(duì)
代碼稍加改進(jìn)就可以避免這種差異的發(fā)生。由于這種差異只會(huì)發(fā)生在觸發(fā)沒有被線程等待在條件變量的時(shí)刻,因此我們只需要掌握好觸發(fā)的時(shí)機(jī)即可。最簡單的做法是增加一個(gè)計(jì)數(shù)器記錄等待線程的個(gè)數(shù),在決定觸發(fā)條件變量前檢查該變量即可。
示例 使用 pthread_cond_wait() 和 pthread_cond_signal()
pthread_mutex_t count_lock;
pthread_cond_t count_nonzero;
unsigned count;
decrement_count()
{
pthread_mutex_lock(&count_lock);
while (count == 0)
pthread_cond_wait(&count_nonzero, &count_lock);
count = count – 1;
pthread_mutex_unlock(&count_lock);
}
increment_count()
{
pthread_mutex_lock(&count_lock);
if (count == 0)
pthread_cond_signal(&count_nonzero);
count = count + 1;
pthread_mutex_unlock(&count_lock);
}
(3) 注意Pthread_cond_wait條件返回時(shí)互斥鎖的解鎖問題
extern int pthread_cond_wait __P ((pthread_cond_t *__cond,pthread_mutex_t *__mutex));
調(diào)用這個(gè)函數(shù)時(shí),線程解開mutex指向的鎖并被條件變量cond阻塞。線程可以被函數(shù)pthread_cond_signal和函數(shù) pthread_cond_broadcast喚醒線程被喚醒后,它將重新檢查判斷條件是否滿足,如果還不滿足,一般說來線程應(yīng)該仍阻塞在這里,被等待被下一次喚醒。如果在多線程中采用pthread_cond_wait來等待時(shí),會(huì)首先釋放互斥鎖,當(dāng)?shù)却男盘?hào)到來時(shí),再次獲得互斥鎖,因此在之后要注意手動(dòng)解鎖。舉例如下:
#include
#include
#include
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; /*初始化互斥鎖*/
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; //初始化條件變量
void *thread1(void *);
void *thread2(void *);
int i=1;
int main(void)
{
pthread_t t_a;
pthread_t t_b;
pthread_create(&t_a,NULL,thread1,(void *)NULL);/*創(chuàng)建進(jìn)程t_a*/
pthread_create(&t_b,NULL,thread2,(void *)NULL); /*創(chuàng)建進(jìn)程t_b*/
pthread_join(t_b, NULL);/*等待進(jìn)程t_b結(jié)束*/
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
exit(0);
}
void *thread1(void *junk)
{
for(i=1;i
{
printf(“IN one\n”);
pthread_mutex_lock(&mutex);//
if(i%3==0)
pthread_cond_signal(&cond);/*,發(fā)送信號(hào),通知t_b進(jìn)程*/
else
printf(“thead1:%d\n”,i);
pthread_mutex_unlock(&mutex);//*解鎖互斥量*/
printf(“Up Mutex\n”);
sleep(3);
}
}
void *thread2(void *junk)
{
while(i
{
printf(“IN two \n”);
pthread_mutex_lock(&mutex);
if(i%3!=0)
pthread_cond_wait(&cond,&mutex);/*等待*/
printf(“thread2:%d\n”,i);
pthread_mutex_unlock(&mutex);
printf(“Down Mutex\n”);
sleep(3);
}
}
輸出如下:
IN one
thead1:1
Up Mutex
IN two
IN one
thead1:2
Up Mutex
IN one
thread2:3
Down Mutex
Up Mutex
IN one
thead1:4
Up Mutex
IN two
IN one
thead1:5
Up Mutex
IN one
Up Mutex
thread2:6
Down Mutex
IN two
thread2:6
Down Mutex
IN one
thead1:7
Up Mutex
IN one
thead1:8
Up Mutex
IN two
IN one
Up Mutex
thread2:9
Down Mutex
注意藍(lán)色的地方,有2個(gè)thread2:6,其實(shí)當(dāng)這個(gè)程序多執(zhí)行幾次,i=3和i=6時(shí)有可能多打印幾個(gè),這里就是競爭鎖造成的了。
(4)另外要注意的Pthread_cond_timedwait等待的是絕對(duì)時(shí)間,這個(gè)和WaitForSingleObject是不同的,Pthread_cond_timedwait在網(wǎng)上也有討論。如下:這個(gè)問題比較經(jīng)典,我把它搬過來。
thread_a :
pthread_mutex_lock(&mutex);
//do something
pthread_mutex_unlock(&mutex)
thread_b:
pthread_mutex_lock(&mutex);
//do something
pthread_cond_timedwait(&cond, &mutex, &tm);
pthread_mutex_unlock(&mutex)
有如上兩個(gè)線程thread_a, thread_b,現(xiàn)在如果a已經(jīng)進(jìn)入了臨界區(qū),而b同時(shí)超時(shí)了,那么b會(huì)從pthread_cond_timedwait返回嗎?如果能返回,那豈不是a,b都在臨界區(qū)?如果不能返回,那pthread_cond_timedwait的定時(shí)豈不是就不準(zhǔn)了?
大家討論有價(jià)值的2點(diǎn)如下:
(1) pthread_cond_timedwait (pthread_cond_t *cv, pthread_mutex_t *external_mutex, const struct timespec *abstime) — This function is a time-based variant of pthread_cond_wait. It waits up to abstime amount of time for cv to be notified. If abstime elapses before cv is notified, the function returns back to the caller with an ETIME result, signifying that a timeout has occurred. Even in the case of timeouts, the external_mutex will be locked when pthread_cond_timedwait returns.
(2) 2.1 pthread_cond_timedwait行為和pthread_cond_wait一樣,在返回的時(shí)候都要再次lock mutex.
2 .2pthread_cond_timedwait所謂的如果沒有等到條件變量,超時(shí)就返回,并不確切。
如果pthread_cond_timedwait超時(shí)到了,但是這個(gè)時(shí)候不能lock臨界區(qū),pthread_cond_timedwait并不會(huì)立即返回,但是在pthread_cond_timedwait返回的時(shí)候,它仍在臨界區(qū)中,且此時(shí)返回值為ETIMEDOUT。
關(guān)于pthread_cond_timedwait超時(shí)返回的問題,我也認(rèn)同觀點(diǎn)2。
附錄:
int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict_attr,void*(*start_rtn)(void*),void *restrict arg);
返回值:若成功則返回0,否則返回出錯(cuò)編號(hào)
返回成功時(shí),由tidp指向的內(nèi)存單元被設(shè)置為新創(chuàng)建線程的線程ID。attr參數(shù)用于制定各種不同的線程屬性。新創(chuàng)建的線程從start_rtn函數(shù)的地址開始運(yùn)行,該函數(shù)只有一個(gè)無指針參數(shù)arg,如果需要向start_rtn函數(shù)傳遞的參數(shù)不止一個(gè),那么需要把這些參數(shù)放到一個(gè)結(jié)構(gòu)中,然后把這個(gè)結(jié)構(gòu)的地址作為arg的參數(shù)傳入。
linux下用C開發(fā)多線程程序,Linux系統(tǒng)下的多線程遵循POSIX線程接口,稱為pthread。
由 restrict 修飾的指針是最初唯一對(duì)指針?biāo)赶虻膶?duì)象進(jìn)行存取的方法,僅當(dāng)?shù)诙€(gè)指針基于之一個(gè)時(shí),才能對(duì)對(duì)象進(jìn)行存取。對(duì)對(duì)象的存取都限定于基于由 restrict 修飾的指針表達(dá)式中。 由 restrict 修飾的指針主要用于函數(shù)形參,或指向由 malloc() 分配的內(nèi)存空間。restrict 數(shù)據(jù)類型不改變程序的語義。 編譯器能通過作出 restrict 修飾的指針是存取對(duì)象的唯一方法的假設(shè),更好地優(yōu)化某些類型的例程。
之一個(gè)參數(shù)為指向線程標(biāo)識(shí)符的指針。
第二個(gè)參數(shù)用來設(shè)置線程屬性。
第三個(gè)參數(shù)是線程運(yùn)行函數(shù)的起始地址。
第四個(gè)參數(shù)是運(yùn)行函數(shù)的參數(shù)。
因?yàn)閜thread不是linux系統(tǒng)的庫,所以在編譯時(shí)注意加上-lpthread參數(shù),以調(diào)用靜態(tài)鏈接庫。
終止線程:
如果在進(jìn)程中任何一個(gè)線程中調(diào)用exit或_exit,那么整個(gè)進(jìn)行會(huì)終止,線程正常的退出方式有:
(1) 線程從啟動(dòng)例程中返回(return)
(2) 線程可以被另一個(gè)進(jìn)程終止(kill);
(3) 線程自己調(diào)用pthread_exit函數(shù)
#include
pthread_exit
線程等待:
int pthread_join(pthread_t tid,void **rval_ptr)
函數(shù)pthread_join用來等待一個(gè)線程的結(jié)束。函數(shù)原型為:
extern int pthread_join __P (pthread_t __th, void **__thread_return);
之一個(gè)參數(shù)為被等待的線程標(biāo)識(shí)符,第二個(gè)參數(shù)為一個(gè)用戶定義的指針,它可以用來存儲(chǔ)被等待線程的返回值。這個(gè)函數(shù)是一個(gè)線程阻塞的函數(shù),調(diào)用它的函數(shù)將一直等待到被等待的線程結(jié)束為止,當(dāng)函數(shù)返回時(shí),被等待線程的資源被收回。
對(duì)于windows線程的創(chuàng)建東西,就不列舉了,msdn上 一搜就出來了。呵呵。今天就講到這里吧,希望是拋磚引玉,大家一起探討,呵呵。部分內(nèi)容我也是參考internet的,特此對(duì)原作者表示感謝!
在linux中運(yùn)用多線程知識(shí)的介紹就聊到這里吧,感謝你花時(shí)間閱讀本站內(nèi)容,更多關(guān)于在linux中運(yùn)用多線程知識(shí),Linux多線程編程指南:提高程序性能的有效手段,linux 多線程環(huán)境下的幾種鎖機(jī)制,淺談linux 多線程編程和 windows 多線程編程的異同的信息別忘了在本站進(jìn)行查找喔。
創(chuàng)新互聯(lián)(cdcxhl.com)提供穩(wěn)定的云服務(wù)器,香港云服務(wù)器,BGP云服務(wù)器,雙線云服務(wù)器,高防云服務(wù)器,成都云服務(wù)器,服務(wù)器托管。精選鉅惠,歡迎咨詢:028-86922220。
標(biāo)題名稱:Linux多線程編程指南:提高程序性能的有效手段(在linux中運(yùn)用多線程知識(shí))
本文地址:http://m.fisionsoft.com.cn/article/djdeeeo.html


咨詢
建站咨詢
