自己動手實現自定義執行緒池

2021-09-05 21:56:23 字數 3741 閱讀 1392

老趙在前幾次的post裡分析了.net的自帶執行緒池,由於.net自帶的執行緒池在底層通過win32api呼叫的windows的程序附帶的執行緒池,所以對於程序,這個執行緒池是唯一的,而且很不幸的是很多.net自身的操作也需要通過這個執行緒池來完成,比如timmer。所以我們來嘗試自己寫乙個執行緒池,這個執行緒池不是靜態的,乙個程序裡可以出現多個執行緒池的例項,我們可以隨時放入要執行的操作,由於沒有系統執行緒池的建立執行緒的頻率的限制,對於大量突發執行緒的頻繁操作來說自定義的執行緒池會比較好用。

首先我們來分析一下實現的原理。執行緒池,顧名思義就是在乙個「池」中儲存了一組可以重複利用的執行緒物件,從而可以節省建立執行緒的開銷。那麼首要需要解決的問題就是復用執行緒了。在.net中我們建立乙個執行緒的方式可以是:

thread t 

=new

thread(method);

我們可以把執行緒看作是方法的乙個包裝,那麼我們要復用執行緒就需要能夠動態的改變執行緒體的方法。為了達到這個目的,我們需要把具體要執行的方法包裝一下,這裡我們就通過delegate來包裝,然後用另外乙個方法來代替被包裝的方法稱為執行緒體。如下:

static object context;

static waitcallback tbodyinstance = new waitcallback(tbody);

}static void tbody(object arg)

如此這般,當執行緒被掛起的時候,我們就能夠修改tbodyinstance,那麼就等於修改了執行緒體要執行的**,而執行緒被掛起之後就相當於是閒置起來了。

class task : idisposable

public event actionworkcomplete; //當執行完成後通知執行緒池執行後續操作的事件

public task()

public void active()

public void setworkitem(waitcallback action, object context)

private void work()

}public void close()

public void dispose()

catch }}

如此這般,當執行緒包裝器初始化的時候,執行緒就啟動並被阻塞掛起,這個時候我們可以設定執行緒執行的方法體委派,並且指定傳遞給執行緒的引數.當執行active方法後執行緒被喚醒,並開始執行執行緒體,當執行緒體執行完之後會開始新迴圈並被繼續掛起,如此這般周而復始。由此我們完成了對執行緒物件的服用。

----------------------我是分割線------------------------

接下來我們需要乙個容器來儲存和管理這些可復用的執行緒包裝器,這個容器也就是所謂的執行緒池了。為了方便描述,以下我們簡稱執行緒包裝器類為執行緒。

我們首先需要乙個物件來儲存所有已經建立出來的執行緒。為了同時方便定位特定的執行緒,我們給每個執行緒增加乙個id的屬性,在建立的時候用guid來賦值,這樣我們就能用dictionary來儲存所有已經建立出來的執行緒引用。

為了減少遍歷,我們將正在工作的執行緒也放在乙個dictionary中,最後把所有空閒的執行緒放在乙個queue裡頭。

由於不能無限量的增加執行緒,所以設定了最大執行緒數的限制,所以如果當需要執行的執行緒超過的時候為了不丟擲異常,我們需要用乙個結構來吧要執行的操作和資料加入佇列,在有空閒執行緒的時候好取出來繼續執行。

所以我們需要重點實現的就是兩個過程,乙個是加入執行緒,乙個是當執行緒執行完畢後所執行的操作。

最後我們來看看完整實現的**:

using system;

using system.collections.generic;

using system.linq;

using system.text;

using system.threading;

namespace taskpooltest

}//設定最小執行緒數

public void setminthread(int value)

}//設定增量

public void setincrement(int value)

}//初始化執行緒池

public taskpool()

}//執行緒執行完畢後的觸發事件

void t_workcomplete(task obj)

else

//設定執行緒的執行委託物件和上下文物件

nt.taskworkitem = item.works;

nt.contextdata = item.context;

//新增到工作字典中

working.add(nt.key, nt);

//喚醒執行緒開始執行

nt.active();

}else

else}}

}//新增工作委託的方法

public void addtaskitem(waitcallback taskitem, object context)

else

else

}//從空閒佇列提出出來設定後開始執行

t = freequeue.dequeue();

working.add(t.key, t);

t.taskworkitem = taskitem;

t.contextdata = context;

t.active();

return;}}

else);}

}}//**資源

public void dispose()

}publicpool.clear();

working.clear();

waitlist.clear();

freequeue.clear();

}//儲存等待佇列的類

class waititem

public object context }}

//執行緒包裝器類

class task : idisposable

public event actionworkcomplete; //執行緒完成一次操作的事件

//用於字典的key

public string key

//初始化包裝器

public task()

//喚醒執行緒

public void active()

//設定執行委託和狀態物件

public void setworkitem(waitcallback action, object context)

//執行緒體包裝方法

private void work()

}//關閉執行緒

public void close()

//**資源

public void dispose()

catch }}

}

最後看看如何使用這個執行緒池類:

using system;

using system.collections.generic;

using system.linq;

using system.text;

using system.threading;

namespace taskpooltest

}, i);

}console.readkey();}}

}

執行的結果

自定義執行緒池

有些時候 jdk自帶的cachedthreadpool fixedthreadpool等執行緒池完成不了我們業務的需求時 可以用threadpoolexecutorg構造自定義的執行緒池。public class usethreadpoolexecutor1 這段 會首先執行任務1,然後把2 3 4...

自定義執行緒池

建立執行緒池方法 儘管executors提供了四種執行緒池建立的方式,但為了實現某些特定的需求,可以自己建立執行緒池。如在阿里的程式設計規範使用executors建立執行緒時,一般會報錯,並提示以下資訊 執行緒池不允許使用executors去建立,而是通過threadpoolexecutor的方式,...

自定義執行緒池

自定義執行緒池建立api 執行緒池建立通過juc的介面 executor 實現,平時我們使用其實現類 threadpoolexecutor 實現自定義執行緒池。常用建構函式 public threadpoolexecutor int corepoolsize,int maximumpoolsize,...