从任务返回而不阻塞UI线程

本文关键字:UI 线程 任务 返回 | 更新日期: 2023-09-27 18:26:26

我有一个返回数据表的方法。我需要所有的sql内容都在线程中运行,然后能够在不阻塞UI线程的情况下传递回数据表。据我所知,当你调用Task.Requal时,它会阻塞UI线程,直到任务完成。我该如何避开这件事。我读过关于使用await和async的文章,但我还没有完全弄清楚如何将其用于任务。

public static DataTable LaunchLocationMasterListReport(ObservableCollection<string> BuiltConditionsList, ObservableCollection<string> BuiltSortList, ObservableCollection<ListBoxCheckBoxItemModel> ColumnsForReport,
    bool LocationNotesCheckBox, ref string reportQuery, ref string reportQueryforSave, ref string reportView, ref string queryCondtions)
{
    queryCondtions = BuildConditionAndSorts(queryCondtions, BuiltConditionsList, BuiltSortList);
    reportQueryforSave = "SELECT * FROM LocationMasterReportView";
    reportView = "LocationMasterReportView";
    reportQuery = "SELECT * FROM LocationMasterReportView " + queryCondtions;
    return LaunchReport(reportQuery, ColumnsForReport).Result;
}
async private static Task<DataTable> LaunchReport(string reportQuery, ObservableCollection<ListBoxCheckBoxItemModel> ColumnsForReport)
{
    SqlConnection myConn = new SqlConnection(Settings.Default.UltrapartnerDBConnectionString);
    DataTable dt = new DataTable();
    string rq = reportQuery;
    Task<DataTable> task = Task.Factory.StartNew(() =>
    {
        using (SqlCommand comm = new SqlCommand(rq, myConn))
        {
            myConn.Open();
            dt.Load(comm.ExecuteReader());
            myConn.Close();
        }
        if (dt.Rows.Count == 0)
        {
            MessageBox.Show("Contains No Results");
            return null;
        }
        foreach (ListBoxCheckBoxItemModel lbc in ColumnsForReport)
        {
            if (!lbc.IsSelected)
            {
                dt.Columns.Remove(lbc.Name.ToString());
            }
        }
        return dt;
    }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);
    return await task;
}

从任务返回而不阻塞UI线程

我同意使用async/await是最好的方法。如前所述,当您等待异步方法时,即使声明的返回类型是Task<T> ,编译器将其转换为T.的隐式返回类型

问题是所有异步方法都必须返回void、Task或Task<T> 。因此,一旦你开始使用它们,你就必须"冒泡"async"方法属性,直到你可以阻塞结果,或者你的方法可以是void或Task(即你已经消耗了实际结果)。

请看这个简单的基于UI的例子:

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }
    private async void Button_Click(object sender, RoutedEventArgs e)
    {
        statusText.Text = "Running";
        statusText.Text = await _ComputeText(true);
        statusText.Text = await _ComputeText(false);
    }
    private static async Task<string> _ComputeText(bool initialTask)
    {
        string result = await Task.Run(() =>
            {
                Thread.Sleep(2000);
                return initialTask ? "Task is done!" : "Idle";
            });
        return result;
    }
}

请注意,按钮事件处理程序"button_Click"被简单地声明为"void"return。但我可以这样做,因为我在该方法中使用异步结果。

在您的情况下,在异步任务完成之前,返回的DataTable不可用。因此,您必须将每个方法声明为"async",一直到实际使用DataTable的任何方法。即使在那里,该方法也需要声明为async,但您不会返回DataTable,因此该方法可以具有"void"或"Task"的返回类型。一个常见的场景是,此方法是一个UI事件处理程序,因此"void"在那里应该很好(在事件处理程序委托中使用时需要使用);您的代码无论如何都不会调用它。但从技术上讲,使用"Task"更为正确,因此,如果在您的上下文中这样做有效,您应该改为使用这种方式。

如果没有一个简洁但完整的例子,很难提供比这更具体的东西。