<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在專案中如果是web請求時候,IIS會自動分配一個執行緒來進行處理,如果很多個應用程式共用公用一個IIS的時候,執行緒分配可能會出現一個問題(當然也是我的需求造成的)
之前在做專案的時候,有一個需求,就是當程式啟動的時候,希望能夠啟動一定數目的執行緒,然後每一個執行緒始終都是在執行的狀態,不進行釋放,然後迴圈去做一些事情。那麼IIS的執行緒管理可能就不是我想要的,因為我想我的一些程式,只用我開啟的執行緒來做工作。也就是說我想模擬一個執行緒池,每次有一個呼叫的時候從自定義執行緒池中取出一個,用完再放回去。
談談我的思路:
1.程式一啟動就通過for迴圈來建立,一定數目的執行緒(這個數目是可以設定的)
2.至少要有三個容器來儲存執行緒,分別是工作執行緒佇列和空閒執行緒佇列以及等待佇列
3.使用執行緒中的AutoResetEvent類,初始每一個執行緒都是unsignaled狀態,執行緒一啟動就一直在迴圈呼叫WaitOne()方法,那麼每次外部呼叫的時候,都呼叫一次這個類範例物件的set,執行緒然後就可以繼續做下面的工作了。
4.至少兩個方法:
第一個開放給外部,讓外部的方法能夠被傳入執行,然後這個方法能夠判斷空閒佇列,等待佇列,以及工作佇列的狀態,如果傳入的時候發現,空閒佇列有空閒的執行緒就直接,將任務委託給空閒佇列的一個執行緒執行,否則把它放到等待佇列。
第二個方法,需要能夠將工作完成的執行緒從工作佇列移動到空閒佇列,然後判斷一下等待佇列是不是有任務,有的話就交給空閒佇列裡面的執行緒來執行。
體思路如上,可以試試先寫一下。
1.因為每個執行緒都有一個AutoResetEvent的範例,所以最好把Thread進行封裝,變成我們自己的Thread。
public class Task { #region Variable //一個AutoResetEvent範例 private AutoResetEvent _locks = new AutoResetEvent(false); //一個Thread範例 private Thread _thread; // 繫結回撥方法,就是外部實際執行的任務 public Action _taskAction; //定義一個事件用來繫結工作完成後的操作,也就是4中所說的工作佇列向空閒佇列移動 public event Action<Task> WorkComplete; /// <summary> ///設定執行緒擁有的Key /// </summary> public string Key { get; set; } #endregion //執行緒需要做的工作 private void Work() { while (true) { //判斷訊號狀態,如果有set那麼 _locks.WaitOne()後的程式就繼續執行 _locks.WaitOne(); _taskAction(); //執行事件 WorkComplete(this); } } #region event //建構函式 public Task() { //スレッドオブジェクトを初期化する _thread = new Thread(Work); _thread.IsBackground = true; Key = Guid.NewGuid().ToString(); //執行緒開始執行 _thread.Start(); } //Set開起訊號 public void Active() { _locks.Set(); } #endregion }
解釋:上面那個Key的作用,因為多個執行緒同時進行的時候,我們並不知道哪一個執行緒的工作先執行完,所以說上面的工作佇列,實際上應該用一個字典來儲存,這樣我們就能在一個執行緒結束工作之後,通過這 裡的KEY(每個執行緒不一樣),來進行定位了。
2.執行緒封裝好了,然後就可以實現執行緒池了
public class TaskPool { #region Variable //建立的執行緒數 private int _threadCount; //空閒執行緒佇列 private Queue<Task> _freeQueue; //工作執行緒字典(為什麼?) private Dictionary<string, Task> _workingDictionary; //空閒佇列,存放需要被執行的外部函數 private Queue<Action> _waitQueue; #endregion #region Event //自定義執行緒池的建構函式 public TaskPool() { _workingDictionary = new Dictionary<string, Task>(); _freeQueue = new Queue<Task>(); _waitQueue = new Queue<Action>(); _threadCount = 10; Task task = null; //產生固定數目的執行緒 for (int i = 0; i < _threadCount; i++) { task = new Task(); //給每一個任務繫結事件 task.WorkComplete += new Action<Task>(WorkComplete); //將每一個新建立的執行緒放入空閒佇列中 _freeQueue.Enqueue(task); } } //執行緒任務完成之後的工作 void WorkComplete(Task obj) { lock (this) { //將執行緒從字典中排除 _workingDictionary.Remove(obj.Key); //將該執行緒放入空閒佇列 _freeQueue.Enqueue(obj); //判斷是否等待佇列中有任務未完成 if (_waitQueue.Count > 0) { //取出一個任務 Action item = _waitQueue.Dequeue(); Task newTask = null; //空閒佇列中取出一個執行緒 newTask = _freeQueue.Dequeue(); // 執行緒執行任務 newTask._taskAction = item; //把執行緒放入到工作佇列當中 _workingDictionary.Add(newTask.Key, newTask); //設定號誌 newTask.Active(); return; } else { return; } } } //新增任務到執行緒池 public void AddTaskItem(Action taskItem) { lock (this) { Task task = null; //判斷空閒佇列是否存線上程 if (_freeQueue.Count > 0) { //存線上程,取出一個執行緒 task = _freeQueue.Dequeue(); //將該執行緒放入工作佇列 _workingDictionary.Add(task.Key, task); //執行傳入的任務 task._taskAction = taskItem; //設定號誌 task.Active(); return; } else { //空閒佇列中沒有空閒執行緒,就把任務放到等待佇列中 _waitQueue.Enqueue(taskItem); return; } } } #endregion }
解釋:這裡的兩個方法,基本符合我的設想,注意每一個方法裡面都有lock操作,這就保證了,多個執行緒進行操作相同的佇列物件的時候,能夠進行互斥。保證一個時間只有一個執行緒在操作。
測試程式碼:
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!"); } }
這裡我執行了20次print操作,看看結果是啥:
從圖中看到20次確實執行了,但是看不到執行緒是哪些,稍微修改一下自定義的執行緒池。
1.在自定義執行緒的建構函式中新增:如下程式碼,檢視哪些執行緒被建立了
public Task() { _thread = new Thread(Work); _thread.IsBackground = true; Key = Guid.NewGuid().ToString(); //執行緒開始執行 _thread.Start(); Console.WriteLine("Thread:"+_thread.ManagedThreadId+" has been created!"); }
2.線上程完成工作方法之後新增如下程式碼,檢視哪些執行緒參與執行任務
private void Work() { while (true) { //判斷訊號狀態,如果有set那麼 _locks.WaitOne()後的程式就繼續執行 _locks.WaitOne(); _taskAction(); Console.WriteLine("Thread:" + Thread.CurrentThread.ManagedThreadId+"workComplete"); //執行事件 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); } }
測試結果:
從結果可以看到,開始和執行的執行緒都是固定的那10個,所以這個程式是可用的。
到此這篇關於C#自定義執行緒池的文章就介紹到這了。希望對大家的學習有所幫助,也希望大家多多支援it145.com。
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45