C#實(shí)現(xiàn)自定義線程池實(shí)例代碼
在項(xiàng)目中如果是web請(qǐng)求時(shí)候,IIS會(huì)自動(dòng)分配一個(gè)線程來進(jìn)行處理,如果很多個(gè)應(yīng)用程序共享公用一個(gè)IIS的時(shí)候,線程分配可能會(huì)出現(xiàn)一個(gè)問題(當(dāng)然也是我的需求造成的)
之前在做項(xiàng)目的時(shí)候,有一個(gè)需求,就是當(dāng)程序啟動(dòng)的時(shí)候,希望能夠啟動(dòng)一定數(shù)目的線程,然后每一個(gè)線程始終都是在運(yùn)行的狀態(tài),不進(jìn)行釋放,然后循環(huán)去做一些事情。那么IIS的線程管理可能就不是我想要的,因?yàn)槲蚁胛业囊恍┏绦颍挥梦议_啟的線程來做工作。也就是說我想模擬一個(gè)線程池,每次有一個(gè)調(diào)用的時(shí)候從自定義線程池中取出一個(gè),用完再放回去。
談?wù)勎业乃悸罚?/p>
1.程序一啟動(dòng)就通過for循環(huán)來創(chuàng)建,一定數(shù)目的線程(這個(gè)數(shù)目是可以配置的)
2.至少要有三個(gè)容器來存儲(chǔ)線程,分別是工作線程隊(duì)列和空閑線程隊(duì)列以及等待隊(duì)列
3.使用線程中的AutoResetEvent類,初始每一個(gè)線程都是unsignaled狀態(tài),線程一啟動(dòng)就一直在循環(huán)調(diào)用WaitOne()方法,那么每次外部調(diào)用的時(shí)候,都調(diào)用一次這個(gè)類實(shí)例對(duì)象的set,線程然后就可以繼續(xù)做下面的工作了。
4.至少兩個(gè)方法:
第一個(gè)開放給外部,讓外部的方法能夠被傳入執(zhí)行,然后這個(gè)方法能夠判斷空閑隊(duì)列,等待隊(duì)列,以及工作隊(duì)列的狀態(tài),如果傳入的時(shí)候發(fā)現(xiàn),空閑隊(duì)列有空閑的線程就直接,將任務(wù)委托給空閑隊(duì)列的一個(gè)線程執(zhí)行,否則把它放到等待隊(duì)列。
第二個(gè)方法,需要能夠?qū)⒐ぷ魍瓿傻木€程從工作隊(duì)列移動(dòng)到空閑隊(duì)列,然后判斷一下等待隊(duì)列是不是有任務(wù),有的話就交給空閑隊(duì)列里面的線程來執(zhí)行。
體思路如上,可以試試先寫一下。
1.因?yàn)槊總€(gè)線程都有一個(gè)AutoResetEvent的實(shí)例,所以最好把Thread進(jìn)行封裝,變成我們自己的Thread。
public class Task { #region Variable //一個(gè)AutoResetEvent實(shí)例 private AutoResetEvent _locks = new AutoResetEvent(false); //一個(gè)Thread實(shí)例 private Thread _thread; // 綁定回調(diào)方法,就是外部實(shí)際執(zhí)行的任務(wù) public Action _taskAction; //定義一個(gè)事件用來綁定工作完成后的操作,也就是4中所說的工作隊(duì)列向空閑隊(duì)列移動(dòng) public event Action<Task> WorkComplete; /// <summary> ///設(shè)置線程擁有的Key /// </summary> public string Key { get; set; } #endregion //線程需要做的工作 private void Work() { while (true) { //判斷信號(hào)狀態(tài),如果有set那么 _locks.WaitOne()后的程序就繼續(xù)執(zhí)行 _locks.WaitOne(); _taskAction(); //執(zhí)行事件 WorkComplete(this); } } #region event //構(gòu)造函數(shù) public Task() { //スレッドオブジェクトを初期化する _thread = new Thread(Work); _thread.IsBackground = true; Key = Guid.NewGuid().ToString(); //線程開始執(zhí)行 _thread.Start(); } //Set開起信號(hào) public void Active() { _locks.Set(); } #endregion }
解釋:上面那個(gè)Key的作用,因?yàn)槎鄠€(gè)線程同時(shí)進(jìn)行的時(shí)候,我們并不知道哪一個(gè)線程的工作先執(zhí)行完,所以說上面的工作隊(duì)列,實(shí)際上應(yīng)該用一個(gè)字典來保存,這樣我們就能在一個(gè)線程結(jié)束工作之后,通過這 里的KEY(每個(gè)線程不一樣),來進(jìn)行定位了。
2.線程封裝好了,然后就可以實(shí)現(xiàn)線程池了
public class TaskPool { #region Variable //創(chuàng)建的線程數(shù) private int _threadCount; //空閑線程隊(duì)列 private Queue<Task> _freeQueue; //工作線程字典(為什么?) private Dictionary<string, Task> _workingDictionary; //空閑隊(duì)列,存放需要被執(zhí)行的外部函數(shù) private Queue<Action> _waitQueue; #endregion #region Event //自定義線程池的構(gòu)造函數(shù) public TaskPool() { _workingDictionary = new Dictionary<string, Task>(); _freeQueue = new Queue<Task>(); _waitQueue = new Queue<Action>(); _threadCount = 10; Task task = null; //產(chǎn)生固定數(shù)目的線程 for (int i = 0; i < _threadCount; i++) { task = new Task(); //給每一個(gè)任務(wù)綁定事件 task.WorkComplete += new Action<Task>(WorkComplete); //將每一個(gè)新創(chuàng)建的線程放入空閑隊(duì)列中 _freeQueue.Enqueue(task); } } //線程任務(wù)完成之后的工作 void WorkComplete(Task obj) { lock (this) { //將線程從字典中排除 _workingDictionary.Remove(obj.Key); //將該線程放入空閑隊(duì)列 _freeQueue.Enqueue(obj); //判斷是否等待隊(duì)列中有任務(wù)未完成 if (_waitQueue.Count > 0) { //取出一個(gè)任務(wù) Action item = _waitQueue.Dequeue(); Task newTask = null; //空閑隊(duì)列中取出一個(gè)線程 newTask = _freeQueue.Dequeue(); // 線程執(zhí)行任務(wù) newTask._taskAction = item; //把線程放入到工作隊(duì)列當(dāng)中 _workingDictionary.Add(newTask.Key, newTask); //設(shè)置信號(hào)量 newTask.Active(); return; } else { return; } } } //添加任務(wù)到線程池 public void AddTaskItem(Action taskItem) { lock (this) { Task task = null; //判斷空閑隊(duì)列是否存在線程 if (_freeQueue.Count > 0) { //存在線程,取出一個(gè)線程 task = _freeQueue.Dequeue(); //將該線程放入工作隊(duì)列 _workingDictionary.Add(task.Key, task); //執(zhí)行傳入的任務(wù) task._taskAction = taskItem; //設(shè)置信號(hào)量 task.Active(); return; } else { //空閑隊(duì)列中沒有空閑線程,就把任務(wù)放到等待隊(duì)列中 _waitQueue.Enqueue(taskItem); return; } } } #endregion }
解釋:這里的兩個(gè)方法,基本符合我的設(shè)想,注意每一個(gè)方法里面都有l(wèi)ock操作,這就保證了,多個(gè)線程進(jìn)行操作相同的隊(duì)列對(duì)象的時(shí)候,能夠進(jìn)行互斥。保證一個(gè)時(shí)間只有一個(gè)線程在操作。
測(cè)試代碼:
class Program { static void Main(string[] args) { TaskPool _taskPool = new TaskPool(); Console.WriteLine(Thread.CurrentThread.ManagedThreadId); for (var i = 0; i < 20; i++) { _taskPool.AddTaskItem(Print); } Console.Read(); } public static void Print() { Console.WriteLine("Do Something!"); } }
這里我執(zhí)行了20次print操作,看看結(jié)果是啥:
從圖中看到20次確實(shí)執(zhí)行了,但是看不到線程是哪些,稍微修改一下自定義的線程池。
1.在自定義線程的構(gòu)造函數(shù)中添加:如下代碼,查看哪些線程被創(chuàng)建了
public Task() { _thread = new Thread(Work); _thread.IsBackground = true; Key = Guid.NewGuid().ToString(); //線程開始執(zhí)行 _thread.Start(); Console.WriteLine("Thread:"+_thread.ManagedThreadId+" has been created!"); }
2.在線程完成工作方法之后添加如下代碼,查看哪些線程參與執(zhí)行任務(wù)
private void Work() { while (true) { //判斷信號(hào)狀態(tài),如果有set那么 _locks.WaitOne()后的程序就繼續(xù)執(zhí)行 _locks.WaitOne(); _taskAction(); Console.WriteLine("Thread:" + Thread.CurrentThread.ManagedThreadId+"workComplete"); //執(zhí)行事件 WorkComplete(this); } }
3.修改客戶端程序
class Program { static void Main(string[] args) { TaskPool _taskPool = new TaskPool(); for (var i = 0; i < 20; i++) { _taskPool.AddTaskItem(Print); } Console.Read(); } public static void Print() { Thread.Sleep(10000); } }
測(cè)試結(jié)果:
從結(jié)果可以看到,開始和執(zhí)行的線程都是固定的那10個(gè),所以這個(gè)程序是可用的。
到此這篇關(guān)于C#自定義線程池的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
在C#項(xiàng)目中調(diào)用C++編寫的動(dòng)態(tài)庫的三種方式
這篇文章給大家介紹了三種方式詳解如何在C#項(xiàng)目中調(diào)用C++編寫的動(dòng)態(tài)庫,文中通過代碼示例給大家介紹的非常詳細(xì),具有一定的參考價(jià)值,需要的朋友可以參考下2024-01-01Unity Undo實(shí)現(xiàn)原理和使用方法詳解
本文將詳細(xì)介紹Unity Undo實(shí)現(xiàn)原理和使用方法,并提供多個(gè)使用例子,幫助開發(fā)者更好地理解和應(yīng)用該功能,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07C#?Unity使用正則表達(dá)式去除部分富文本的代碼示例
正則表達(dá)式在我們?nèi)粘i_發(fā)中的用處不用多說了吧,下面這篇文章主要給大家介紹了關(guān)于C#?Unity使用正則表達(dá)式去除部分富文本的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-03-03Unity?UGUI的StandaloneInputModule標(biāo)準(zhǔn)輸入模塊組件使用示例
這篇文章主要為大家介紹了Unity?UGUI的StandaloneInputModule標(biāo)準(zhǔn)輸入模塊組件使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08c#實(shí)現(xiàn)輸出的字符靠右對(duì)齊的示例
下面小編就為大家分享一篇c#實(shí)現(xiàn)輸出的字符靠右對(duì)齊的示例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2017-12-12Unity Shader相交算法實(shí)現(xiàn)簡(jiǎn)易防能量盾
這篇文章主要為大家詳細(xì)介紹了Unity Shader相交算法實(shí)現(xiàn)簡(jiǎn)易防能量盾,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-04-04