Task.RunSynchronously 有啥用?
Posted
技术标签:
【中文标题】Task.RunSynchronously 有啥用?【英文标题】:What is Task.RunSynchronously for?Task.RunSynchronously 有什么用? 【发布时间】:2019-03-16 12:13:29 【问题描述】:我只是想知道方法是什么?在什么样的场景下我可以使用这种方法。
我最初的想法是 RunSynchronously
用于调用异步方法并同步运行该方法,而不会像 .wait()
那样导致死锁问题。
但是,根据MSDN,
通常,任务在线程池线程上异步执行,不会阻塞调用线程。通过调用 RunSynchronously() 方法执行的任务与当前的 TaskScheduler 相关联,并在调用线程上运行。如果目标调度器不支持在调用线程上运行该任务,则该任务将按计划调度执行,调用线程将阻塞直到任务执行完毕
如果任务要在调用线程上运行,为什么这里需要一个 TaskScheduler?
【问题讨论】:
看起来它会让调度程序决定何时运行它,而不是根据您复制的描述立即在当前线程上运行它。调度器会在这里做出决定,而不是立即同步运行。 它对大多数异步方法没有任何用处,因为它们返回“热”任务(即已经运行)您只能将Run
或 RunSynchronously
与“冷”任务一起使用还没开始。
Stephen Cleary blog 似乎已经涵盖了它。顶部的免责声明很有趣 在这篇博文中绝对没有推荐用于现代代码的内容。如果您正在寻找最佳实践,请继续前进;这里没什么可看的。所以它似乎是遗产
方法 RunSynchronously
是在 .NET 4.5 中引入 async await
流之前创建的,显然这个想法是允许同步执行“冷”任务并尝试使用相同的线程上下文,并且指定的调度程序。与.StartNew()
相同,它已过时。除非你需要创建一个冷任务并在当前线程中同步运行它,使用当前的TaskScheduler
,否则使用它是没有意义的。
@Fabjan 两者都有一个小案例,因为它们接受 TaskScheduler 参数。如果您想使用自定义调度程序,例如,limed DOP 调度程序。即使这样,也有更好的选择,例如具有有限 DOP 的 ActionBlock
【参考方案1】:
RunSynchronously
将何时启动任务的决定委托给当前任务调度程序(或作为参数传递的调度程序)。
我不确定它为什么会存在(可能是内部使用或遗留使用),但在当前版本的 .NET 中很难想出一个有用的用例。 @Fabjan has a possible explanation in his comment 提问。
RunSynchronously
要求调度程序同步运行它,但调度程序很可能会忽略提示并在线程池线程中运行它,您当前的线程将同步阻塞,直到它完成。
调度程序不必在当前线程上运行它,也不必立即运行它,尽管我认为这是常见调度程序(ThreadPoolTaskScheduler 和常见 UI 调度程序)上会发生的事情。
RunSynchronously
也会在任务已经启动或完成/出错时抛出异常(这意味着您将无法在异步方法上使用它)。
此代码可能会阐明不同的行为:
Wait
和 Result
根本不运行任务,它们只是等待当前线程上的任务完成并阻塞它直到完成,所以如果我们想比较,我们可以比较 Start
和Wait
转RunSynchronously
:
class Scheduler : TaskScheduler
protected override void QueueTask(Task task) =>
Console.WriteLine("QueueTask");
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
Console.WriteLine("TryExecuteTaskInline");
return false;
protected override IEnumerable<Task> GetScheduledTasks() => throw new NotImplementedException();
static class Program
static void Main()
var taskToStart = new Task(() => );
var taskToRunSynchronously = new Task(() => );
taskToStart.Start(new Scheduler());
taskToRunSynchronously.RunSynchronously(new Scheduler());
如果您尝试注释 Start 或 RunSynchronously
并运行代码,您将看到 Start
尝试将任务排队到调度程序,而 RunSynchronously
将尝试内联执行它,如果失败(返回 false) ,它只会排队。
【讨论】:
【参考方案2】:首先让我们看一下这段代码:
public async static Task<int> MyAsyncMethod()
await Task.Delay(100);
return 100;
//Task.Delay(5000).RunSynchronously(); // bang
//Task.Run(() => Thread.Sleep(5000)).RunSynchronously(); // bang
// MyAsyncMethod().RunSynchronously(); // bang
var t = new Task(() => Thread.Sleep(5000));
t.RunSynchronously(); // works
在此示例中,我们尝试在以下任务上调用 RunSynchronously
:
async await
创建的另一个承诺任务
与代表的“冷”任务
创建后会有什么状态?
等待激活 等待运行 等待激活 已创建所有 'hot' 和 promise 任务都是以WaitingForActivation
或WaitingToRun
状态创建的。热门任务也与任务调度程序相关联。
方法RunSynchronously
只知道如何处理包含委托且状态为Created
的“冷”任务。
结论:
方法RunSynchronously
可能出现在没有“热”任务或者它们没有被广泛使用并且是为特定目的而创建的时候。
我们可能希望在需要自定义TaskScheduler
的“冷”任务时使用它,否则它已经过时且毫无用处。
为了同步运行“热”任务(我们应该尽量避免),我们可以使用task.GetAwaiter().GetResult()
。这与.Result
相同,但作为奖励,它返回原始异常而不是AggregateException
。尽管如此,“同步优于异步”并不是最佳选择,应尽可能避免。
【讨论】:
可能值得注意的是,Thread
(基本上)已被Task
(Task.Delay()
) 取代,因此Thread.Sleep
现在也可以被视为旧版。这就是它与旧版 RunSynchronously
一起使用的原因
GetAwaiter:此方法旨在供编译器使用,而不是在应用程序代码中使用。 docs.microsoft.com/en-us/dotnet/api/…
@Ares:请参阅this answer,了解 task.Result 与 task.GetAwaiter().GetResult()。以上是关于Task.RunSynchronously 有啥用?的主要内容,如果未能解决你的问题,请参考以下文章