我正在使用Asp.Net 4.0。
我还有一个HashSet,我想从中创建并异步运行任务,等待它们全部完成并应用超时。下面的代码是我到目前为止的代码,但鉴于有很多方法可以使用TPL来修饰猫,可以改进,还是有任何关键问题?
TaskWrapper[] taskWrappers = new TaskWrapper[nodeCount];
Task<ResponseWrapper>[] tasks = new Task<ResponseWrapper>[nodeCount];
int i = 0;
foreach (var n in rt.Nodes)
{
tasks[i] = Task<ResponseWrapper>.Factory.StartNew(() => MyMethod.Submit(n.Data, timeout));
taskWrappers[i].LeadPointID = n.LeadPointID;
// other wrapper stuff;
taskWrappers[i].Task = tasks[i];
i++;
}
Task.WaitAll(tasks, timeout);
阅读了所提供的文章后,似乎这种代码结构将成为首选的工作方式:
class MyService2
{
public int CalculateMandelbrot()
{
// Tons of work to do in here!
for (int i = 0; i != 10000000; ++i)
;
return 42;
}
}
private async void MyButton_Click(object sender, EventArgs e)
{
await Task.Run(() => myService.CalculateMandelbrot());
}
然而,正如我之前所指出的那样,我无法访问VS2012,所以作为一个起点,是否存在与此模式相当的VS2010?
答案 0 :(得分:2)
Task.WaitAll
将阻止您的主线程,直到所有其他任务完成。这可以用于服务更多请求的一个较少的线程。
相反,您应该使用await Task.WhenAll
。但是,WhenAll
没有超时的超时。 A possible workaround是将它与Task.Delay
结合使用,然后等待所有任务完成或延迟任务完成:
await Task.WhenAny(Task.WhenAll(tasks), Task.Delay(timeout));
如果您无法访问VS 2012,那么另一种方法是将延续任务附加到WhenAny
,然后返回该任务。该框架将负责其余部分。
public Task<string> Post()
{
var tasks = //...
var continuation = Task.WhenAny(Task.WhenAll(tasks), Task.Delay(timeout))
.ContinueWith(whenAnyTask =>
{
//the rest of your code
return "Hello";
};
return continuation;
}
更重要的是:不要在ASP.NET应用程序上使用Task.Run
(或StartNew
)!
您正在从线程池中获取线程以更快地为客户端提供服务,但这些线程可用于为更多客户端提供服务!因此,您要告诉其他客户&#34;等一下,我首先将我的资源集中在为这个人服务上#34;你也引入了很多不必要的开销。
在Web应用程序中,一次只使用一个线程来为客户端提供服务,并且仅将异步代码用于异步I / O,这是一种很好的做法。
编辑:Aron
.net(x86)中的每个线程使用1MB RAM或4MB(x64)。即使是具有16GB内存的相当强大的服务器也只能运行4000个线程(假设其他地方没有内存使用)。这意味着如果每个请求运行4个线程,则只能同时处理1000个请求。
而node.js服务器(使用与ASP.Net async / await类似的模型)可以轻松地在单个线程上处理超过10000个并发请求(读取更小的内存占用量)。
有关更详细的说明,请参阅Stephen Cleary的Task.Run Etiquette Examples: Don't Use Task.Run in the Implementation。