C#和任务 - UI线程挂起 - 预异步/等待关键字

时间:2012-12-20 19:11:09

标签: c# asynchronous task-parallel-library async-await

当我无法访问我用来检索数据的客户端lib时,我正试图了解异步获取一组数据的正确代码。我指定了一个端点和一个日期范围,我应该检索一个播放列表列表。我现在所拥有的东西在Start()调用之后永远不会回来。注意:这是在WinForm中运行的。我试图更好地理解任务,而不只是想跳到等待或BackgroundWorker。我知道我在某个地方迷路了。

    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线程。

    private void GoButtonClick(object sender, EventArgs e)
    {
        string baseUrl = "http://someserver/api";
        var startDateTime = this._startDateTimePicker.Value;
        var endDateTime = this._endDateTimePicker.Value;
        var token = Task.Factory.CancellationToken;

        var context = TaskScheduler.FromCurrentSynchronizationContext();
        Task.Factory.StartNew(() =>
            {
                var client = new PlaylistExportClient(baseUrl);
                _queueDataGridView.DataSource = client.GetPlaylistsByDateRange(startDateTime, endDateTime).ToList();

            },token,TaskCreationOptions.None,context);

    }

2 个答案:

答案 0 :(得分:3)

我建议您使用基于任务的异步模式。这很简单:

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;
}

请注意,这将阻止线程池线程;如果您可以修改库以使用GetPlaylistsByDateRangeAsync方法,则可以提高效率。

编辑:如果您坚持使用.NET 4.0,则可以安装Microsoft.Bcl.Async以获得完整的async / await功能。如果 - 出于某种莫名其妙的原因 - 您仍然无法使用async / await,那么您可以这样做:

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);
}

但请注意,使用此方法时,错误处理会更复杂。

答案 1 :(得分:2)

看起来您正在后台线程中分配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);