C#了解阻塞的UI和异步/等待与Task.Run的关系吗?

时间:2018-09-15 10:26:42

标签: c# asynchronous async-await task

我正在尝试进行一些与UI线程分离的异步I / O工作。我读过的地方:

  • 1)对于受CPU约束的代码,您需要等待在 Task.Run方法使用后台线程。如计算素数 数字
  • 2)对于绑定到I / O的代码,您等待一个操作,该操作返回一个 Task或异步方法内部的Task。如等待 网络或数据库

所以我做到了:

// in windows form UI
private async void btnImport_Click(object sender, EventArgs e) {
    // [...]
    List<DataRow> rows = await importer.ImportDataAsync(123, 456);
    // [...]
}

// in Importer.ImportDataAsync:
public async Task<List<DataRow>> ImportDataAsync(int parent, int child, CancellationToken token = default(CancellationToken)) {

    // [...]
    List<DataRow> list = await RealImportFromDB(parent, child);
    return list;
    // [...]
}


public List<DataRow> RealImportFromDB(int p, int c) {

    List<DataRow> rowList;
    // here fetch the rows from DB over slow network
    // and return the row list
    return rowList;
}

通过这种方法,UI被阻止。 如果我这样叫RealImportFromDB(...)

List<DataRow> l = await Task.Run(() => RealImportFromDB(parent, child));

UI未被阻止,但与恕我直言的上方的点2)冲突。

我在哪里做错了事?

最诚挚的问候,亚历克斯

2 个答案:

答案 0 :(得分:1)

public List<DataRow> RealImportFromDB(int p, int c)是对数据库的阻塞调用,因此要异步执行该数据库,请使用#1,其中将调用包装在Task.Run中,这将释放Ui线程,预期的

  

通过这种方法,UI被阻止。如果我致电RealImportFromDB(...)

这是因为该方法不适用于异步调用,因此不会返回TaskTask<T>,这是进行异步调用的常见要求

您的代码await RealImportFromDB(parent, child)不正确,这是编译错误,因为您只能等待调用,从而实现GetAwaiter()内部检查(thisthis)并且最常见的情况是返回TaskTask<T>,还有其他类型

让我们尝试理解您的两个陈述:

  

1)对于受CPU约束的代码,您需要等待通过Task.Run方法在后台线程上启动的操作。例如计算素数

这是您当前正在执行的操作,通常在客户端中执行以释放Ui线程,而处理在后台进行,但这仍将使用线程池线程来执行,这不如Ui重要线程,但仍然是系统资源

  

2)对于绑定到I / O的代码,您需要等待一个操作,该操作将在异步方法内部返回Task或Task。例如等待网络或数据库

要实现此目的,您需要一个方法,该方法默认为Async并返回一个TaskTask<T>,这些方法是所有数据框架的一部分,对于当今的每个sync方法,都有一个对应的async方法发起异步执行,它们是真正的IO调用,它们不使用线程,因为处理不在同一进程中,因此跨网络/进程边界,因此调用线程不必等待,它只需要返回并选择到达的结果(任何线程池线程,而不是调度线程)。在内部,此类方法使用TaskCompletionSource<T>When to use TaskCompletionSource),该机制具有在网络通话完成时通知呼叫者的机制

答案 1 :(得分:0)

  

要实现此目的,您需要一个方法,默认情况下为Async   返回任务或任务

非常感谢,这是我的麻烦。我的RealImportFromDB(...)方法不是异步方法,因为它处理的似乎是旧的专有库,似乎尚未准备好进行异步调用。

这些是我的思想: 等待ImportDataAsync(...)的结果时,其中所有被调用的内容(例如RealImportFromDB(...))都从UI线程也是分派。可以这么说:ImportDataAsync(...)中的所有内容都在第二个非阻塞线程中被封装/运行在上。

@others:是的,您是对的,我代码中的示例甚至不会编译。摆弄很多,所以代码示例无法显示更改的全部内容,对此很抱歉:-}

相关问题