引用:http://blog.csdn.net/sq_zhuyi/article/details/6869661
在WEB開發(fā)中,為了減少頁面等待時(shí)間提高用戶體驗(yàn),我們往往會把一些浪費(fèi)時(shí)間的操作放到新線程中在后臺運(yùn)行。
簡單的實(shí)現(xiàn)代碼就是:

//代碼一new Thread(()=>{ //do something}).Start();View Code但是對于一個(gè)請求量大的網(wǎng)址這樣做是很不現(xiàn)實(shí)的——每一個(gè)操作都要開啟一個(gè)新線程,最終會因CPU不堪重負(fù)而使網(wǎng)站掛掉。
更好的做法是使用線程隊(duì)列。
對于線程隊(duì)列ThreadPool.QueueUserWorkItem很多人應(yīng)該都不陌生,下邊看微軟的解釋:
將方法排入隊(duì)列以便執(zhí)行,并指定包含該方法所用數(shù)據(jù)的對象。此方法在有線程池線程變得可用時(shí)執(zhí)行。
它的作用就是將一些操作放入當(dāng)前線程之外的另外一個(gè)線程中執(zhí)行,它的使用方法很簡單:

//代碼二ThreadPool.QueueUserWorkItem(stat => { //do something}, null);View Code它相對代碼一的優(yōu)點(diǎn)是會利用已經(jīng)創(chuàng)建過的空閑的線程,如果沒有空閑就排隊(duì),而不會盲目的一直創(chuàng)建下去。
但是它并沒有擺脫“創(chuàng)建新線程”的問題:過多的線程會占用更多的資源。由此我們不難想到,我們?yōu)槭裁床蛔约焊銈€(gè)隊(duì)列,讓它們在同一個(gè)線程中逐個(gè)執(zhí)行?對此,我寫了個(gè)簡單的實(shí)現(xiàn)類:

1 public class BackgroundTasks 2 { 3 PRivate class TaskEntity 4 { 5 public TaskEntity(Action<object> func, object data) 6 { 7 this.Function = func; 8 this.Data = data; 9 }10 public Action<object> Function;11 public object Data;12 }13 static Queue<TaskEntity> list = new Queue<TaskEntity>();14 15 static BackgroundTasks()16 {17 Thread th = new Thread(RunTask);18 th.IsBackground = true;19 th.Start();20 }21 static void RunTask()22 {23 while (true)24 {25 if (list.Count == 0)26 {27 Thread.Sleep(1000);28 }29 else30 {31 TaskEntity entity;32 lock (list)33 {34 entity = list.Dequeue();35 }36 try37 {38 entity.Function(entity.Data);39 }40 catch { }41 Thread.Sleep(10);42 }43 }44 }45 46 public static void Add(Action<object> func, object data)47 {48 lock (list)49 {50 list.Enqueue(new TaskEntity(func, data));51 }52 }53 54 }View Code該類的使用很簡單:
BackgroundTasks.Add((obj)=>{ Console.WriteLine("這個(gè)任務(wù)的添加時(shí)間是:{0}", obj as DateTime);}, DateTime.Now);還有一個(gè)“實(shí)例版”的,就是針對每個(gè)方法,分別創(chuàng)建一個(gè)任務(wù)隊(duì)列:

1 public class BackgroundTasks01<T> 2 { 3 private Action<T> Function; 4 5 private Queue<T> list = new Queue<T>(); 6 7 public BackgroundTasks01(Action<T> func) 8 { 9 this.Function = func;10 11 Thread th = new Thread(RunTask);12 th.IsBackground = true;13 th.Start();14 }15 private void RunTask()16 {17 while (true)18 {19 if (list.Count == 0)20 {21 Thread.Sleep(1000);22 }23 else24 {25 T data;26 lock (list)27 {28 data = list.Dequeue();29 }30 try31 {32 Function(data);33 }34 catch { }35 Thread.Sleep(10);36 }37 }38 }39 40 public void Add(T data)41 {42 lock (list)43 {44 list.Enqueue(data);45 }46 }47 48 }View Codeblog類:

class Blog{ private int _blogId = 0; public int BlogId { get { return _blogId; } set { _blogId = value; } } private string _blogName = ""; public string BlogName { get { return _blogName; } set { _blogName = value; } }}View Code調(diào)用示例:

//方法二:var bg = new BackgroundTasks01<Blog>((blog) =>{ Console.WriteLine("BlogName:{0},BlogId:{1}", blog.BlogName, blog.BlogId);});int i = 0;while (i++ < 100){ bg.Add(new Blog() { BlogId = i, BlogName = "Default" });}Console.ReadLine();bg.Add(new Blog() { BlogId = 1000, BlogName = "張三" });bg.Add(new Blog() { BlogId = 1001, BlogName = "李四" });View Code這個(gè)設(shè)計(jì)既解決了異步執(zhí)行,又解決了占用資源的問題。
但是世界上沒有完美的東西,代碼也是如此,由于隊(duì)列中的任務(wù)是單線程執(zhí)行,可能會導(dǎo)致某些任務(wù)在很長時(shí)間后才會被執(zhí)行到,或者重啟IIS導(dǎo)致很多任務(wù)還沒有被執(zhí)行就被丟棄。
無論怎么,這種設(shè)計(jì)還是適用于很多“一般情況”。
|
新聞熱點(diǎn)
疑難解答
圖片精選