網站首頁 編程語言 正文
在項目中如果是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個,所以這個程序是可用的。
原文鏈接:https://www.cnblogs.com/dcz2015/p/5263006.html
相關推薦
- 2022-10-16 Python?Flask框架使用介紹_python
- 2022-11-02 Android?Studio模擬器運行apk文件_Android
- 2023-02-04 C語言設計實現掃描器的自動機的示例詳解_C 語言
- 2022-12-14 C++利用類實現矩陣的數乘,乘法以及點乘_C 語言
- 2022-07-09 python連接clickhouse數據庫的兩種方式小結_python
- 2022-06-22 React?數據共享useContext的實現_React
- 2022-03-29 python中apply函數詳情_python
- 2022-08-27 python基礎篇之pandas常用基本函數匯總_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支