我正在尝试进行一些与UI线程分离的异步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));
UI未被阻止,但与恕我直言的上方的点2)冲突。
我在哪里做错了事?
最诚挚的问候,亚历克斯
答案 0 :(得分:1)
public List<DataRow> RealImportFromDB(int p, int c)
是对数据库的阻塞调用,因此要异步执行该数据库,请使用#1,其中将调用包装在Task.Run
中,这将释放Ui线程,预期的
通过这种方法,UI被阻止。如果我致电RealImportFromDB(...)
这是因为该方法不适用于异步调用,因此不会返回Task
或Task<T>
,这是进行异步调用的常见要求
您的代码await RealImportFromDB(parent, child)
不正确,这是编译错误,因为您只能等待调用,从而实现GetAwaiter()
内部检查(this和this)并且最常见的情况是返回Task
或Task<T>
,还有其他类型
让我们尝试理解您的两个陈述:
1)对于受CPU约束的代码,您需要等待通过Task.Run方法在后台线程上启动的操作。例如计算素数
这是您当前正在执行的操作,通常在客户端中执行以释放Ui线程,而处理在后台进行,但这仍将使用线程池线程来执行,这不如Ui重要线程,但仍然是系统资源
2)对于绑定到I / O的代码,您需要等待一个操作,该操作将在异步方法内部返回Task或Task。例如等待网络或数据库
要实现此目的,您需要一个方法,该方法默认为Async并返回一个Task
或Task<T>
,这些方法是所有数据框架的一部分,对于当今的每个sync方法,都有一个对应的async方法发起异步执行,它们是真正的IO调用,它们不使用线程,因为处理不在同一进程中,因此跨网络/进程边界,因此调用线程不必等待,它只需要返回并选择到达的结果(任何线程池线程,而不是调度线程)。在内部,此类方法使用TaskCompletionSource<T>
(When to use TaskCompletionSource),该机制具有在网络通话完成时通知呼叫者的机制
答案 1 :(得分:0)
要实现此目的,您需要一个方法,默认情况下为Async 返回任务或任务
非常感谢,这是我的麻烦。我的RealImportFromDB(...)
方法不是异步方法,因为它处理的似乎是旧的专有库,似乎尚未准备好进行异步调用。
这些是我的思想:
等待ImportDataAsync(...)
的结果时,其中所有被调用的内容(例如RealImportFromDB(...)
)都从UI线程也是分派。可以这么说:ImportDataAsync(...)
中的所有内容都在第二个非阻塞线程中被封装/运行在上。
@others:是的,您是对的,我代码中的示例甚至不会编译。摆弄很多,所以代码示例无法显示更改的全部内容,对此很抱歉:-}