C#了解被阻止的UI和async / await与Task.Run的问题?

问题描述 投票:2回答:2

我正在尝试从UI线程中分离出一些异步I / O工作。在某处我读到:

  • 1)对于CPU绑定代码,您等待使用Task.Run方法在后台线程上启动的操作。比如计算素数
  • 2)对于I / O绑定代码,您等待在异步方法内返回任务或任务的操作。如等待网络或数据库

所以我这样做了:

// 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));

用户界面没有被阻止但是与恕我直言的2)点相冲突。

我哪里做错了?

最好的问候,亚历克斯

c# asynchronous async-await task
2个回答
1
投票

public List<DataRow> RealImportFromDB(int p, int c)是对数据库的阻塞调用,所以要异步执行它,你已经使用了#1,你在Task.Run中包含了调用,这将释放Ui线程,如预期的那样

使用此方法,UI被阻止。如果我调用RealImportFromDB(...)

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

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

让我们试着理解你的两个陈述:

1)对于CPU绑定代码,您等待使用Task.Run方法在后台线程上启动的操作。比如计算素数

这是你目前正在做的事情,并且通常在客户端完成以释放Ui线程,而处理在后台进行,但这仍然会使用线程池线程来执行,这不如Ui线程重要,但是仍然是系统资源

2)对于I / O绑定代码,您等待在异步方法内返回任务或任务的操作。如等待网络或数据库

要实现这一点,你需要一个方法,默认情况下是Async并返回一个TaskTask<T>,这样的方法是所有数据框架的一部分,对于现在每个同步方法都有一个相应的异步方法来启动异步执行,它们是真正的IO调用,他们不使用线程,因为处理不在同一个进程中,它跨越网络/进程边界,所以调用线程不需要等待,它只需要返回并选择结果,当它到达时(任何线程池线程,没有必要的调度线程)。在内部这样的方法使用TaskCompletionSource<T>When to use TaskCompletionSource),它具有在网络呼叫完成时通知呼叫者的机制


0
投票

要实现这一点,您需要一个方法,默认情况下为Async并返回任务或任务

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

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

@others:是的你是对的,我的代码中的样本甚至都不会编译。摆弄了很多,所以代码示例没有显示所有更改的内容,对不起: - }

© www.soinside.com 2019 - 2024. All rights reserved.