Geo*_*uer 4 c# asp.net multithreading task-parallel-library async-await
我想我不懂东西.我原本以为Task.Yield()强制为一个任务启动一个新的线程/上下文,但是在重新阅读这个答案时,它似乎只是强制该方法是异步的.它仍然是在相同的背景下.
什么是正确的方法 - 在asp.net进程中 - 并行创建和运行多个任务而不会导致死锁?
换句话说,假设我有以下方法:
async Task createFileFromLongRunningComputation(int input) {
//many levels of async code
}
Run Code Online (Sandbox Code Playgroud)
当某个POST路由被命中时,我想同时启动上述方法3次,立即返回,但是当完成所有这三个时都要记录.
我想我需要把这样的东西放进我的行动中
public IHttpAction Post() {
Task.WhenAll(
createFileFromLongRunningComputation(1),
createFileFromLongRunningComputation(2),
createFileFromLongRunningComputation(3)
).ContinueWith((Task t) =>
logger.Log("Computation completed")
).ConfigureAwait(false);
return Ok();
Run Code Online (Sandbox Code Playgroud)
}
需要了解什么createFileFromLongRunningComputation?我原以为Task.Yield是正确的,但显然不是.
将并发工作卸载到不同线程的正确方法是使用Task.Runrossipedia建议.
The best solutions for background processing in ASP.Net (where your AppDomain can be recycled/shut down automatically together with all your tasks) are in Scott Hanselman and Stephen Cleary's blogs (e.g. HangFire)
However, you could utilize Task.Yield together with ConfigureAwait(false) to achieve the same.
All Task.Yield does is return an awaiter that makes sure the rest of the method doesn't proceed synchronously (by having IsCompleted return false and OnCompleted execute the Action parameter immediately). ConfigureAwait(false) disregards the SynchronizationContext and so forces the rest of the method to execute on a ThreadPool thread.
If you use both together you can make sure an async method returns a task immediately which will execute on a ThreadPool thread (like Task.Run):
async Task CreateFileFromLongRunningComputation(int input)
{
await Task.Yield().ConfigureAwait(false);
// executed on a ThreadPool thread
}
Run Code Online (Sandbox Code Playgroud)
Edit:
George Mauer pointed out that since Task.Yield returns YieldAwaitable you can't use ConfigureAwait(false) which is a method on the Task class.
You can achieve something similar by using Task.Delay with a very short timeout, so it wouldn't be synchronous but you wouldn't waste much time:
async Task CreateFileFromLongRunningComputation(int input)
{
await Task.Delay(1).ConfigureAwait(false);
// executed on a ThreadPool thread
}
Run Code Online (Sandbox Code Playgroud)
A better option would be to create a YieldAwaitable that simply disregards the SynchronizationContext the same as using ConfigureAwait(false) does:
async Task CreateFileFromLongRunningComputation(int input)
{
await new NoContextYieldAwaitable();
// executed on a ThreadPool thread
}
public struct NoContextYieldAwaitable
{
public NoContextYieldAwaiter GetAwaiter() { return new NoContextYieldAwaiter(); }
public struct NoContextYieldAwaiter : INotifyCompletion
{
public bool IsCompleted { get { return false; } }
public void OnCompleted(Action continuation)
{
var scheduler = TaskScheduler.Current;
if (scheduler == TaskScheduler.Default)
{
ThreadPool.QueueUserWorkItem(RunAction, continuation);
}
else
{
Task.Factory.StartNew(continuation, CancellationToken.None, TaskCreationOptions.PreferFairness, scheduler);
}
}
public void GetResult() { }
private static void RunAction(object state) { ((Action)state)(); }
}
}
Run Code Online (Sandbox Code Playgroud)
This isn't a recommendation, it's an answer to your Task.Yield questions.
| 归档时间: |
|
| 查看次数: |
4793 次 |
| 最近记录: |