简体   繁体   English

在不阻塞 UI 线程的情况下从任务返回

[英]Returning from a task without blocking UI thread

I have a method that returns a datatable.我有一个返回数据表的方法。 I need for all the sql stuff to run in a thread and then be able to pass back a datatable without it blocking the UI thread.我需要所有 sql 的东西在一个线程中运行,然后能够在不阻塞 UI 线程的情况下传回数据表。 From my understanding, when you call Task.Result it blocks the UI thread until the task has completed.根据我的理解,当您调用 Task.Result 时,它会阻塞 UI 线程,直到任务完成。 How would I get around this.我将如何解决这个问题。 I read about using await and async but I haven't quite figured out how to use that with the task yet.我读过关于使用 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;
}

I agree that using async/await is the best approach here.我同意在这里使用 async/await 是最好的方法。 As noted, when you await an async method, then even though the declared return type is a Task<T>, the compiler translates this into an implicit return type of T.如前所述,当您等待异步方法时,即使声明的返回类型是 Task<T>,编译器也会将其转换为 T 的隐式返回类型。

The gotcha is that all async methods must return void, Task, or Task<T>.问题是所有异步方法都必须返回 void、Task 或 Task<T>。 So once you start using them, you have to "bubble up" the "async" method attribute until you get to a point where you can either block on the result or your method can be void or Task (ie you've consumed the actual result).所以一旦你开始使用它们,你必须“冒泡”“async”方法属性,直到你可以阻塞结果或者你的方法可以是 void 或 Task(即你已经消耗了实际的结果)。

Please see this simple UI-based example:请参阅这个基于 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;
    }
}

Note that the button event handler, "Button_Click", is simply declared as "void" return.请注意,按钮事件处理程序“Button_Click”被简单地声明为“void”返回。 But I can do that because I consume the asynchronous result in that method.但是我可以这样做,因为我在该方法中使用了异步结果。

In your case, the returned DataTable is not available until the asynchronous task has completed.在您的情况下,返回的 DataTable 在异步任务完成之前不可用。 So you have to declare each method as "async" all the way back to whatever method is actually doing to do something with the DataTable.因此,您必须将每个方法声明为“异步”,一直返回到实际使用 DataTable 执行某些操作的任何方法。 Even there, the method will need to be declared as async, but you won't be returning the DataTable, and so that method can have a return type of "void" or "Task".即使在那里,该方法也需要声明为异步,但您不会返回 DataTable,因此该方法可以具有“void”或“Task”的返回类型。 A common scenario is for this method to be a UI event handler, so "void" should be fine there (and will be required for use in an event handler delegate);一个常见的场景是这个方法是一个 UI 事件处理程序,所以“void”应该没问题(并且需要在事件处理程序委托中使用); your code isn't calling it anyway.无论如何,您的代码并没有调用它。 But it's technically more correct to use "Task" instead, so if in your context that works, you should do it that way instead.但从技术上讲,使用“任务”更正确,因此如果在您的上下文中有效,您应该这样做。

Without a concise-but-complete example, it's hard to offer anything more specific than that.没有一个简洁但完整的例子,很难提供比这更具体的东西。

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

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