【问题标题】:Getting a Cannot await void, on a method that I have want to await on在我想要等待的方法上获得无法等待无效
【发布时间】:2017-07-03 04:38:42
【问题描述】:

我在一个编写 WPF 应用程序的团队中。我们必须这样做,以便当用户隐藏/显示不同的列时,它将反映在其中一个视图的 ReportViewer 控件中。在测试中我们发现将数据添加到 ReportViewer 的数据源需要很长时间;有时大约几秒钟到一分钟。我认为对用户来说太长了。所以我正在尝试使用 C# 的异步和等待。但是,当我将 await 应用于进程 hog 的行然后编译它时,我从 C# 编译器收到一个错误,“无法等待'void'”。在这种情况下,我无法更改 .NET 框架返回的内容,它是无效的。那么我该如何处理这种情况呢?代码如下:

private async Task GenerateReportAsync()
{
    DataSet ds = new DataSet();
    DataTable dt = new DataTable();
    dt.Clear();
    int iCols = 0;
    //Get the column names
    if (Columns.Count == 0)     //Make sure it isn't populated twice
    {
        foreach (DataGridColumn col in dataGrid.Columns)
        {
            if (col.Visibility == Visibility.Visible)
            {
                Columns.Add(col.Header.ToString());     //Get the column heading
                iCols++;
            }
        }
    }
    //Create a DataTable from the rows
    var itemsSource = dataGrid.ItemsSource as IEnumerable<Grievance>;
    if (this.rptViewer != null)
    {
        rptViewer.Reset();
    }
    rptViewer.LocalReport.DataSources.Clear();
    if (m_rdl != null)
    {
        m_rdl.Dispose();
    }
    Columns = GetFieldOrder();
    m_rdl = CoreUtils.GenerateRdl(Columns, Columns);
    rptViewer.LocalReport.LoadReportDefinition(m_rdl);
    //the next line is what takes a long time
    await rptViewer.LocalReport.DataSources.Add(new ReportDataSource("MyData", CoreUtils.ToDataTable(itemsSource)));
    rptViewer.RefreshReport();
}

【问题讨论】:

  • 如果您认为等待需要很长时间的事情可以解决您的问题,那么您一定对await 所做的事情有一些错误的看法。你能描述一下你对await 的看法吗?只有等待已经异步的东西才有意义;你相信 await makes 不是异步的东西变成异步操作吗?它没有。
  • 据我了解,使用 async/await 可以将控制权返回给 UI 线程,而不是挂起,等待一些漫长的工作过程完成。不过谢谢你的提问。
  • 我的观点是,如果函数返回 并且 已经是异步的,那么 它应该不会花费太多时间。如果它是无效返回、同步和高延迟,那么await 没有任何意义。 Await 管理一个现有的异步操作。如果你有一个高延迟的操作,你希望是异步的,await 不会帮助你。您将不得不弄清楚如何通过其他机制使其异步。

标签: c# wpf asynchronous async-await


【解决方案1】:

对于本质上是同步的方法,您需要将它们包装在您自己的Task 中,以便您可以等待它。在你的情况下,我会使用Task.Run:

await Task.Run(() => 
{
   rptViewer.LocalReport.DataSources.Add(new ReportDataSource("MyData", CoreUtils.ToDataTable(itemsSource)));
});

还有其他方法可以生成任务,但这个可能是最简单的。如果这更新了 UI 组件,它可能会抛出异常,因为这些操作必须发生在 UI 线程上。在这种情况下,请尝试将与 UI 相关的部分从长时间运行的部分中移开,并仅将较长的部分包装在 Task 中。

【讨论】:

  • 这可能会引发跨线程异常,因为您正在从后台线程访问报表查看器等 UI 组件。
  • @ScottChamberlain 当然,我已经注意到了。
  • @BradleyDotNET 我采纳了您的建议并提出以下建议:DataTable dtTmp = null;等待Task.Run(() => dtTmp = CoreUtils.ToDataTable(itemsSource)); rptViewer.LocalReport.DataSources.Add(new ReportDataSource("MyData", dtTmp));我不知道在 UI 组件上使用 await 会导致异常。谢谢你的头!以上将控制权迅速交还给 UI 线程。再次感谢..
  • @Rod 导致异常的不是等待,而是 Tas.Run 造成的。
【解决方案2】:

我几乎可以肯定整条线不是慢线,CoreUtils.ToDataTable(itemsSource) 更有可能是慢线,这是需要改进的部分。

您没有包含ToDataTable 的来源,所以我不能确定最好的方法是什么,第一个也是最好的选择是编写一个新版本的函数,它在内部利用异步调用来创建一个允许你等待它的函数。

var data = await CoreUtils.ToDataTableAsync(itemsSource)
rptViewer.LocalReport.DataSources.Add(new ReportDataSource("MyData", data));

第二个性能较差的选项是使用Task.Run 在后台线程上执行慢速部分,然后返回 UI 线程根据需要设置数据源。

var data = await Task.Run(() => CoreUtils.ToDataTable(itemsSource));
rptViewer.LocalReport.DataSources.Add(new ReportDataSource("MyData", data));

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多