我有一个同步方法GetReports()
,该返回值将用于设置UI控件的数据源。可能需要一段时间才能运行。如下异步调用它是惯用的方式吗?
var l = new List<...>();
await Task.Run(() => l = GetReports().ToList());
UIControl.DataSource = l;
答案 0 :(得分:2)
您应该使用Microsoft的Reactive Framework(又名Rx)-NuGet System.Reactive.Windows.Forms
并添加using System.Reactive.Linq;
-然后您可以执行以下操作:
IDisposable subscription =
Observable
.Start(() => GetReports().ToList())
.ObserveOn(UIControl)
.Subscribe(list => UIControl.DataSource = list);
这很好地推送到一个新线程,然后将其拉回,然后更新DataSource
。
如果您需要在取消之前取消,只需致电subscription.Dispose();
。
如果您对GetReports
的呼叫可以取消,则可以执行以下操作:
IDisposable subscription =
Observable
.FromAsync(ct => GetReports(ct))
.Select(x => x.ToList())
.ObserveOn(UIControl)
.Subscribe(list => UIControl.DataSource = list);
现在呼叫subscription.Dispose()
也将取消任务。
答案 1 :(得分:1)
如果您响应的是 UI ,并且要运行长时间运行的CPU工作负载(而不是这样的可伸缩性),那么这很好,并且可以实现您想要的。基本上会
await
之后执行所有操作
尽管 Tasks 不是线程,但您会发现这将从 Thread Pool 中窃取线程来完成工作量,并且可以释放 UI线程,直到完成
您也可以使用较旧的样式Task.Run
和ContinueWith
做同样的事情。
还有另一种流派,如果您将TaskCreationOptions
与LongRunning
一起使用时,TaskFactory.StartNew
会提示您要使用的默认TaskScheduler
在线程池之外创建一个线程。这样可以为线程池提供更多资源。
说TaskFactory.StartNew
是 Task 创建方法的祖父,它有自己的怪癖,您可能仅应在特别需要这样做时才使用它所以。我只会坚持你所拥有的。
最后一个注释,尽管将工作量包装在一个方法中并命名为async
似乎是一个好主意,但这通常不是一个好主意。如果必须包装,则最好由调用者决定。因此,您再次做了正确的事。 Stephen Cleary 讨论了Fake Async
和Async Wrappers
以及您不需要这样做的原因