从任务返回而不会阻塞UI线程 [英] Returning from a task without blocking UI thread

查看:98
本文介绍了从任务返回而不会阻塞UI线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个返回数据表的方法.我需要所有sql东西在一个线程中运行,然后能够传递回一个数据表而不会阻塞UI线程.据我了解,当您调用Task.Result时,它将阻塞UI线程,直到任务完成.我将如何解决这个问题.我读过有关使用await和async的信息,但我还没有弄清楚如何在任务中使用它.

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. From my understanding, when you call Task.Result it blocks the UI thread until the task has completed. 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.

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

推荐答案

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

I agree that using async/await is the best approach here. 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.

要注意的是,所有异步方法必须返回void,Task或Task< T>.因此,一旦开始使用它们,就必须冒泡"异步"方法属性,直到达到可以阻塞结果或者方法可能为空或Task(即您已经消耗了实际值)为止的地步.结果).

The gotcha is that all async methods must return void, Task, or 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 (i.e. you've consumed the actual result).

请参见以下基于UI的简单示例:

Please see this simple UI-based example:

/// <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"返回.但是我可以这样做,因为我在该方法中使用了异步结果.

Note that the button event handler, "Button_Click", is simply declared as "void" return. But I can do that because I consume the asynchronous result in that method.

对于您而言,返回的DataTable在异步任务完成之前不可用.因此,您必须将每个方法声明为异步",直到将其实际用于对DataTable做某事的任何方法为止.即使在那里,该方法也需要声明为异步,但是您不会返回DataTable,因此该方法的返回类型可以为"void"或"Task".常见的情况是,此方法将成为UI事件处理程序,因此"void"应该在那里(并且将在事件处理程序委托中使用);您的代码仍然没有调用它.但是从技术上来说,改为使用任务"更为正确,因此,如果在您认为合适的情况下,则应采用这种方式.

In your case, the returned DataTable is not available until the asynchronous task has completed. 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. 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". 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); 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.

这篇关于从任务返回而不会阻塞UI线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆