天天看點

linux線程池的實作——bobo

什麼是線程池

應用程式可以有多個線程,這些線程在休眠狀态中需要耗費大量時間來等待事件發生。其他線程可能進入睡眠狀态,并且僅定期被喚醒以輪循更改或更新狀态資訊,然後再次進入休眠狀态。為了簡化對這些線程的管理,.NET架構為每個程序提供了一個線程池,一個線程池有若幹個等待操作狀态,當一個等待操作完成時,線程池中的輔助線程會執行回調函數。線程池中的線程由系統管理,程式員不需要費力于線程管理,可以集中精力處理應用程式任務。

線程池是一種多線程處理形式,處理過程中将任務添加到隊列,然後在建立線程後自動啟動這些任務。線程池線程都是背景線程.每個線程都使用預設的堆棧大小,以預設的優先級運作,并處于多線程單元中.如果某個線程在托管代碼中空閑(如正在等待某個事件),則線程池将插入另一個輔助線程來使所有處理器保持繁忙.如果所有線程池線程都始終保持繁忙,但隊列中包含挂起的工作,則線程池将在一段時間後建立另一個輔助線程但線程的數目永遠不會超過最大值.超過最大值的線程可以排隊,但他們要等到其他線程完成後才啟動 

在什麼情況下使用線程池? 

   1.單個任務處理的時間比較短 

   2.将需處理的任務的數量大 

使用線程池的好處: 

     1.減少在建立和銷毀線程上所花的時間以及系統資源的開銷 

   2.如不使用線程池,有可能造成系統建立大量線程而導緻消耗完系統記憶體以及”過度切換”。

何時不使用線程池線程:

如果需要使一個任務具有特定優先級

如果具有可能會長時間運作(并是以阻塞其他任務)的任務

如果需要将線程放置到單線程單元中(線程池中的線程均處于多線程單元中)

如果需要永久辨別來辨別和控制線程,比如想使用專用線程來終止該線程,将其挂起或按名稱發現它

應用範圍

1、需要大量的線程來完成任務,且完成任務的時間比較短。 WEB伺服器完成網頁請求這樣的任務,使用線程池技術是非常合适的。因為單個任務小,而任務數量巨大,你可以想象一個熱門網站的點選次     數。 但對于長時間的任務,比如一個Telnet連接配接請求,線程池的優點就不明顯了。因為Telnet會話時間比線程的建立時間大多了。

2、對性能要求苛刻的應用,比如要求伺服器迅速響應客戶請求。

3、接受突發性的大量請求,但不至于使伺服器是以産生大量線程的應用。突發性大量客戶請求,在沒有線程池情況下,将産生大量線程,雖然理論上大部分作業系統線程數目最大值不是問題,短時間内産生大量線程可能使記憶體到達極限,并出現"OutOfMemory"的錯誤。

為什麼需要線程池

       目前的大多數網絡伺服器,包括Web伺服器、Email伺服器以及資料庫伺服器等都具有一個共同點,就是機關時間内必須處理數目巨大的連接配接請求,但處理時間卻相對較短。 

       傳統多線程方案中我們采用的伺服器模型則是一旦接受到請求之後,即建立一個新的線程,由該線程執行任務。任務執行完畢後,線程退出,這就是是“即時建立,即時銷毀”的政策。盡管與建立程序相比,建立線程的時間已經大大的縮短,但是如果送出給線程的任務是執行時間較短,而且執行次數極其頻繁,那麼伺服器将處于不停的建立線程,銷毀線程的狀态。 

我們将傳統方案中的線程執行過程分為三個過程:T1、T2、T3。 

T1:線程建立時間 

T2:線程執行時間,包括線程的同步等時間 

T3:線程銷毀時間 

            那麼我們可以看出,線程本身的開銷所占的比例為(T1+T3) / (T1+T2+T3)。如果線程執行的時間很短的話,這比開銷可能占到20%-50%左右。如果任務執行時間很頻繁的話,這筆開銷将是不可忽略的。

       除此之外,線程池能夠減少建立的線程個數。通常線程池所允許的并發線程是有上界的,如果同時需要并發的線程數超過上界,那麼一部分線程将會等待。而傳統方案中,如果同時請求數目為2000,那麼最壞情況下,系統可能需要産生2000個線程。盡管這不是一個很大的數目,但是也有部分機器可能達不到這種要求。 

       是以線程池的出現正是着眼于減少線程池本身帶來的開銷。線程池采用預建立的技術,在應用程式啟動之後,将立即建立一定數量的線程(N1),放入空閑隊列中。這些線程都是處于阻塞(Suspended)狀态,不消耗CPU,但占用較小的記憶體空間。當任務到來後,緩沖池選擇一個空閑線程,把任務傳入此線程中運作。當N1個線程都在處理任務後,緩沖池自動建立一定數量的新線程,用于處理更多的任務。在任務執行完畢後線程也不退出,而是繼續保持在池中等待下一次的任務。當系統比較空閑時,大部分線程都一直處于暫停狀态,線程池自動銷毀一部分線程,回收系統資源。 

       基于這種預建立技術,線程池将線程建立和銷毀本身所帶來的開銷分攤到了各個具體的任務上,執行次數越多,每個任務所分擔到的線程本身開銷則越小,不過我們另外可能需要考慮進去線程之間同步所帶來的開銷。

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

以下提供了實作線程的源代碼以及測試demo,注釋非常詳細,

隻需一步就能夠建立線程池,然後再添加任務。

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

下載下傳線程池源代碼:線程池源代碼

#include <stdio.h>
#include <unistd.h>

#include "thread_pool.h"

void *task_test(void *arg)
{
	printf("\t\tworking on task %d\n", (int)arg);
	sleep(1);			/*休息一秒,延長任務的執行時間*/
	return NULL;
}

void thread_pool_demo(void)
{
	pool_t pool;
	int i = 0;

	pool_init(&pool, 2);
	sleep(1);
	for(i = 0; i < 5; i++){
		sleep(1);
		pool_add_task(&pool, task_test, (void *)i);
	}
	sleep(4);

	pool_uninit(&pool);
}

int main (int argc, char *argv[])
{  
	thread_pool_demo();
	return 0;
}