您的位置:首页 > 产品设计 > UI/UE

ThreadPool.QueueUserWorkItem的性能问题

2012-08-13 16:34 489 查看
在WEB开发中,为了减少页面等待时间提高用户体验,我们往往会把一些浪费时间的操作放到新线程中在后台运行。

简单的实现代码就是:

[csharp] view plaincopyprint?

//代码一

new Thread(()=>{

//do something

}).Start();

但是对于一个请求量大的网址这样做是很不现实的——每一个操作都要开启一个新线程,最终会因CPU不堪重负而使网站挂掉。

更好的做法是使用线程队列。

对于线程队列 ThreadPool.QueueUserWorkItem 很多人应该都不陌生,下边看微软的解释:

将方法排入队列以便执行,并指定包含该方法所用数据的对象。此方法在有线程池线程变得可用时执行。

它的作用就是将一些操作放入当前线程之外的另外一个线程中执行,它的使用方法很简单:

[csharp] view plaincopyprint?

//代码二

ThreadPool.QueueUserWorkItem(stat => {

//do something

}, null);

它相对代码一的优点是会利用已经创建过的空闲的线程,如果没有空闲就排队,而不会盲目的一直创建下去。

但是它并没有摆脱“创建新线程”的问题:过多的线程会占用更多的资源。由此我们不难想到,我们为什么不自己搞个队列,让它们在同一个线程中逐个执行?对此,我写了个简单的实现类:

[csharp] view plaincopyprint?

public class BackgroundTasks

{

private class TaskEntity

{

public TaskEntity(Action<object> func, object data)

{

this.Function = func;

this.Data = data;

}

public Action<object> Function;

public object Data;

}

static Queue<TaskEntity> list = new Queue<TaskEntity>();

static BackgroundTasks()

{

Thread th = new Thread(RunTask);

th.IsBackground = true;

th.Start();

}

static void RunTask()

{

while (true)

{

if (list.Count==0)

{

Thread.Sleep(1000);

}

else

{

TaskEntity entity;

lock (list)

{

entity = list.Dequeue();

}

try

{

entity.Function(entity.Data);

}

catch { }

Thread.Sleep(10);

}

}

}

public static void Add(Action<object> func, object data)

{

lock (list)

{

list.Enqueue(new TaskEntity(func, data));

}

}

}

该类的使用很简单:

BackgroundTasks.Add((obj)=>{

Console.WriteLine("这个任务的添加时间是:{0}", obj as DateTime);

}, DateTime.Now);

还有一个“实例版”的,就是针对每个方法,分别创建一个任务队列:

[csharp] view plaincopyprint?

public class BackgroundTasks<T>

{

private Action<T> Function;

private Queue<T> list = new Queue<T>();

public BackgroundTasks(Action<T> func)

{

this.Function = func;

Thread th = new Thread(RunTask);

th.IsBackground = true;

th.Start();

}

private void RunTask()

{

while (true)

{

if (list.Count == 0)

{

Thread.Sleep(1000);

}

else

{

T data;

lock (list)

{

data = list.Dequeue();

}

try

{

Function(data);

}

catch { }

Thread.Sleep(10);

}

}

}

public void Add(T data)

{

lock (list)

{

list.Enqueue(data);

}

}

}

调用示例:

[csharp] view plaincopyprint?

var bg = new BackgroundTasks<Blog>((blog) => {

Console.WriteLine(blog.BlogId);

});

int i = 0;

while (i++ < 1000)

{

bg.Add(new Blog() { BlogId = i });

}

这个设计既解决了异步执行,又解决了占用资源的问题。

但是世界上没有完美的东西,代码也是如此,由于队列中的任务是单线程执行,可能会导致某些任务在很长时间后才会被执行到,或者重启IIS导致很多任务还没有被执行就被丢弃。

无论怎么,这种设计还是适用于很多“一般情况”。

原文地址:blog.csdn.net/sq_zhuyi/article/details/6869661
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: