繁体   English   中英

C#和任务-UI线程挂起-Pre-Async / Await关键字

[英]C# and Tasks - UI Thread Hang - Pre-Async/Await keywords

当我无权访问用于检索数据的客户端库时,我试图了解什么是正确的代码来异步获取一组数据。 我指定一个端点和一个日期范围,并且应该检索一个播放列表。 在Start()调用之后,我现在再也不会回来。 注意:这是在WinForm中运行。 我试图更好地了解Tasks,而不仅仅是想跳到等待或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);

    }

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

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

但是,请注意,使用这种方法的错误处理更加复杂。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM