C#和任务-UI线程挂起-预异步/等待关键字
我试图理解,当我无法访问用于检索数据的客户机库时,异步获取一组数据的正确代码是什么。我指定了一个端点和一个日期范围,我应该检索一个播放列表。在Start()调用之后,我现在拥有的东西再也不会回来了。注意:这是在WinForm中运行的。我试图更好地理解任务,而不仅仅是想跳转到等待者或后台工作人员。我知道我在什么地方迷路了C#和任务-UI线程挂起-预异步/等待关键字,c#,asynchronous,task-parallel-library,async-await,C#,Asynchronous,Task Parallel Library,Async Await,我试图理解,当我无法访问用于检索数据的客户机库时,异步获取一组数据的正确代码是什么。我指定了一个端点和一个日期范围,我应该检索一个播放列表。在Start()调用之后,我现在拥有的东西再也不会回来了。注意:这是在WinForm中运行的。我试图更好地理解任务,而不仅仅是想跳转到等待者或后台工作人员。我知道我在什么地方迷路了 private void GoButtonClick(object sender, EventArgs e) { string baseUrl =
private void GoButtonClick(object sender, EventArgs e)
{
string baseUrl = "http://someserver/api";
var startDateTime = this._startDateTimePicker.Value;
var endDateTime = this._endDateTimePicker.Value;
_getPlaylistsFunc = delegate()
{
var client = new PlaylistExportClient(baseUrl);
return client.GetPlaylistsByDateRange(startDateTime, endDateTime).ToList();
};
var task = new Task<List<Playlist>>(_getPlaylistsFunc);
task.ContinueWith((t) => DisplayPlaylists(t.Result));
task.Start();
}
private void DisplayPlaylists(List<Playlist> playlists)
{
_queueDataGridView.DataSource = playlists;
}
看起来您正在为后台线程中的UI控件的属性赋值。这通常是个坏消息。当您这样做时,WPF通常会抛出一个异常,但不确定WinForms 在后台线程中捕获数据,但在将其分配给UI控件之前切换回主UI线程。尝试使用以下方法将数据发布到UI线程
var uiSync = SynchronizationContext.Current;
Task.Factory.StartNew(() =>
{
var client = new PlaylistExportClient(baseUrl);
var list = client.GetPlaylistsByDateRange(...).ToList();
uiSync.Post(() => _queueDataGridView.DataSource = list, null);
},token,TaskCreationOptions.None,context);
我建议您使用基于任务的异步模式。这很简单:
private async void GoButtonClick(object sender, EventArgs e)
{
string baseUrl = "http://someserver/api";
var startDateTime = this._startDateTimePicker.Value;
var endDateTime = this._endDateTimePicker.Value;
var playlists = await Task.Run(() =>
{
var client = new PlaylistExportClient(baseUrl);
return client.GetPlaylistsByDateRange(startDateTime, endDateTime).ToList();
});
_queueDataGridView.DataSource = playlists;
}
请注意,这将阻止线程池线程;如果可以将库修改为具有getplaylisbydaterangeasync
方法,则可以提高效率
编辑:如果您被困在.NET 4.0上,您可以安装以获得完整的async
/wait
功能。如果出于某种无法解释的原因,您仍然不能使用async
/wait
,那么您可以这样做:
private void GoButtonClick(object sender, EventArgs e)
{
string baseUrl = "http://someserver/api";
var startDateTime = this._startDateTimePicker.Value;
var endDateTime = this._endDateTimePicker.Value;
var context = TaskScheduler.FromCurrentSynchronizationContext();
Task.Run(() =>
{
var client = new PlaylistExportClient(baseUrl);
return client.GetPlaylistsByDateRange(startDateTime, endDateTime).ToList();
}).ContinueWith(t =>
{
_queueDataGridView.DataSource = t.Result;
}, context);
}
但是,请注意,这种方法的错误处理更为复杂。如果在
getplaylisbydaterange
中放置断点,您是否看到它实际上被调用了?您需要将同步上下文传递给延续,以便它在UI线程而不是另一个线程池线程中运行。除此之外,我在这里没有看到任何真正的错误。您的更新在UI线程上运行所有任务。只有第二个应该在那里运行。我明白-我想。Post()不是我的context.Woops上的方法。好的,您所调用的上下文变量实际上是一个调度程序。我将用代码更新我的答案以获取同步上下文。看起来这非常接近。谢谢。我正在计算Post()lambda参数。现在IDE正在报告匿名函数上的不正确签名。仍然锁定我的WinForm。我所做的唯一更改是在Post()中添加(对象状态)调用。我确实在尝试了解如何在异步前/等待时完成此操作。但也有很好的信息。我根据问题中指定的async wait
标记给出了此答案。如果您想要异步前的async
答案,请选择Task.ContinueWith
(与TaskScheduler.FromCurrentSynchronizationContext
)或者BackgroundWorker
就足够了。async
代码比这两个选项都干净得多。使用ContinueWith
方法查看更新的代码答案。+1.更新的代码正是海报想要的,除了Task.Run
需要是Task.Factory.StartNew
运行没有存在于.NET 4.0中。是的,.NET 4.0中没有。即使在安装之后,它仍然不可用。如果有人坚持使用.NET 4.0,那么他通常坚持使用VS2010,您对他的引用是无用的
private void GoButtonClick(object sender, EventArgs e)
{
string baseUrl = "http://someserver/api";
var startDateTime = this._startDateTimePicker.Value;
var endDateTime = this._endDateTimePicker.Value;
var context = TaskScheduler.FromCurrentSynchronizationContext();
Task.Run(() =>
{
var client = new PlaylistExportClient(baseUrl);
return client.GetPlaylistsByDateRange(startDateTime, endDateTime).ToList();
}).ContinueWith(t =>
{
_queueDataGridView.DataSource = t.Result;
}, context);
}