Ответ 1
правильный способ отключить параллельную работу для разных потоков - использовать Task.Run
в качестве предлагаемого rossipedia.
Лучшие решения для фоновой обработки в ASP.Net (где ваш AppDomain
можно автоматически перерабатывать/выключать вместе со всеми вашими задачами) находятся в Скотт Гензельман и Стивен Клири блоги (например, HangFire)
Однако для достижения этого можно использовать Task.Yield
вместе с ConfigureAwait(false)
.
Все Task.Yield
это возвращает awaiter, который гарантирует, что остальная часть метода не будет выполняться синхронно (путем IsCompleted
return false
и OnCompleted
немедленно выполнить параметр Action
). ConfigureAwait(false)
игнорирует SynchronizationContext
и поэтому заставляет остальную часть метода выполнять поток ThreadPool
.
Если вы используете оба метода вместе, вы можете убедиться, что метод async
немедленно возвращает задачу, которая будет выполняться в потоке ThreadPool
(например, Task.Run
):
async Task CreateFileFromLongRunningComputation(int input)
{
await Task.Yield().ConfigureAwait(false);
// executed on a ThreadPool thread
}
Изменить:
Джордж Мауэр отметил, что, поскольку Task.Yield
возвращает YieldAwaitable
, вы не можете использовать ConfigureAwait(false)
, который является методом класса Task
.
Вы можете добиться чего-то подобного, используя Task.Delay
с очень коротким таймаутом, поэтому он не будет синхронным, но вы не потратите много времени:
async Task CreateFileFromLongRunningComputation(int input)
{
await Task.Delay(1).ConfigureAwait(false);
// executed on a ThreadPool thread
}
Лучшим вариантом было бы создать YieldAwaitable
, который просто игнорирует SynchronizationContext
так же, как при использовании ConfigureAwait(false)
делает:
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)(); }
}
}
Это не рекомендация, это ответ на ваши вопросы Task.Yield.