是否可以等待未声明为异步的 IO 操作?如果没有,我该怎么办?
Is it possible to await an IO operation that is not declared as async? If not, what should I do?
我是 C# 异步编程的新手,但我仍然对一些事情感到困惑。
我读到在 .NET 4.5 之后,不再建议将 APM 和 EAP 用于新开发,因为 TAP 应该取代它们 (source)。
我想我了解 async/await 的工作原理并且我能够使用它们来执行具有异步方法的 IO 操作。例如,我可以编写等待 HttpWebClient 的 GetStringAsync 结果的异步方法,因为它被声明为异步方法。太棒了
我的问题是:如果我们有一个 IO 操作发生在未声明为异步的方法中怎么办?像这样:假设我有一个 API 有一个方法
string GetResultFromWeb()
从 Web 查询内容。我有很多不同的查询要做,我 必须 使用此方法来完成。然后我需要处理每个查询结果。我知道如果那是一个异步方法我会这样做:
Task<string> getResultTask = GetResultFromWeb(myUrl);
// Do whatever I need to do that doesn't need the query result
string result = await getResultTask;
Process(result);
但由于它不是,我不能等待它 -- 它告诉我字符串不可等待。所以我的问题是:是否有任何方法可以异步执行这些 IO 操作,而不必为每个查询创建一个线程?如果可以的话,我想创建尽可能少的线程,而不必阻塞任何线程。
我发现这样做的一种方法是实施 APM,遵循 Jeffrey Richter 的 this article,然后在我的 Begin 方法中调用 ThreadPool.QueueWorkItem(GetResultFromWeb, asyncResult)。像这样:
public class A {
private void DoQuery(Object ar){
AsyncResult<string> asyncResult = (AsyncResult<string>) ar;
string result = GetResultFromWeb();
asyncResult.SetAsCompleted(result, false);
}
public IAsyncResult BeginQuery(AsyncCallback){
AsyncResult<string> asyncResult = new AsyncResult<string>(callback, this);
ThreadPool.QueueUserWorkItem(DoQuery, asyncResult);
return asyncResult;
}
public string EndQuery(IAsyncResult ar){
AsyncResult<string> asyncResult = (AsyncResult<string>)ar;
return asyncResult.EndInvoke();
}
}
然后我使用 AsyncEnumerator 并开始 (BeginQuery) 多个查询并在每个查询完成时处理结果(使用 yield return / EndQuery)。这似乎运作良好。但是在阅读了这么多 APM 已过时之后,我想知道如何使用 TAP 来做到这一点。另外,这种APM方法有什么问题吗?
谢谢!
一种更简单的方法来完成您正在寻找的事情是使用 Task
class 调用该方法。在您的情况下,它看起来像这样:
Task<string> getResultTask = Task.Run<string>(()=>GetResultFromWeb(myUrl));
// Do whatever I need to do that doesn't need the query result
string result = await getResultTask;
Process(result);
虽然这将像您的 IAsyncResult
选项那样创建另一个线程,但这大大简化了过程。
您的 API 是异步的,使用旧的 Begin/End 模型。这符合 TPL via
Task.Factory.FromAsync<string>(BeginQuery, EndQuery)
哪个returns一个Task<string>
哪个你可以await
。
what if we have an IO operation that happens in a method that is not declared as async?
在这种情况下,I/O 操作是阻塞的。换句话说,GetResultFromWeb
阻塞调用线程。在我们完成其余部分时请记住这一点...
I must use this method to do so.
据此我推断您不能编写异步的 GetResultFromWebAsync
方法。所以任何执行网络请求的线程必须被阻塞。
is there any way of performing these IO operations asynchronously without having to create one thread for each query?
最自然的做法是写一个GetResultFromWebAsync
方法。由于这是不可能的,您的选择是:阻止调用线程,或阻止其他线程(即线程池线程)。阻塞线程池线程是一种我称之为 "fake asynchrony" 的技术——因为它看起来是异步的(即不阻塞 UI 线程)但实际上不是(即它只是阻塞线程池线程)。
If I could, I'd like to create as less threads as possible, without having to block any of the threads.
鉴于限制,这是不可能的。如果您必须使用GetResultFromWeb
方法,而该方法阻塞调用线程,则线程必须 被屏蔽了。
One way I found to do so was by implementing APM, following this article from Jeffrey Richter and then, in my Begin method, I call ThreadPool.QueueWorkItem(GetResultFromWeb, asyncResult).
在这种情况下,您的代码公开了一个异步 API (begin/end),但在实现中它只是在线程池线程上调用 GetResultFromWeb
。即,它是伪异步。
This seems to work well.
它可以工作,但不是真正的异步。
But after reading so much that APM is obsolete, I was wondering how could I do this using TAP.
正如其他人所指出的,有一种更简单的方法可以将工作安排到线程池:Task.Run
。
真正的异步是不可能的,因为您必须使用 阻塞 方法。因此,您所能做的就是解决方法 - 假异步,a.k.a。阻塞线程池线程。最简单的方法是:
Task<string> getResultTask = Task.Run(() => GetResultFromWeb(myUrl));
// Do whatever I need to do that doesn't need the query result
string result = await getResultTask;
Process(result);
(比 APM 和 AsyncEnumerator 更简洁的代码)
请注意,我 不 建议创建一个 GetResultFromWebAsync
使用伪异步实现的方法。返回任务的异步后缀方法应该遵循 Task-based Asynchronous Pattern guidelines,这意味着 true 异步。
换句话说,正如我在我的博客上更详细地描述的那样,use Task.Run
to invoke a method, not to implement a method。
我是 C# 异步编程的新手,但我仍然对一些事情感到困惑。 我读到在 .NET 4.5 之后,不再建议将 APM 和 EAP 用于新开发,因为 TAP 应该取代它们 (source)。
我想我了解 async/await 的工作原理并且我能够使用它们来执行具有异步方法的 IO 操作。例如,我可以编写等待 HttpWebClient 的 GetStringAsync 结果的异步方法,因为它被声明为异步方法。太棒了
我的问题是:如果我们有一个 IO 操作发生在未声明为异步的方法中怎么办?像这样:假设我有一个 API 有一个方法
string GetResultFromWeb()
从 Web 查询内容。我有很多不同的查询要做,我 必须 使用此方法来完成。然后我需要处理每个查询结果。我知道如果那是一个异步方法我会这样做:
Task<string> getResultTask = GetResultFromWeb(myUrl);
// Do whatever I need to do that doesn't need the query result
string result = await getResultTask;
Process(result);
但由于它不是,我不能等待它 -- 它告诉我字符串不可等待。所以我的问题是:是否有任何方法可以异步执行这些 IO 操作,而不必为每个查询创建一个线程?如果可以的话,我想创建尽可能少的线程,而不必阻塞任何线程。
我发现这样做的一种方法是实施 APM,遵循 Jeffrey Richter 的 this article,然后在我的 Begin 方法中调用 ThreadPool.QueueWorkItem(GetResultFromWeb, asyncResult)。像这样:
public class A {
private void DoQuery(Object ar){
AsyncResult<string> asyncResult = (AsyncResult<string>) ar;
string result = GetResultFromWeb();
asyncResult.SetAsCompleted(result, false);
}
public IAsyncResult BeginQuery(AsyncCallback){
AsyncResult<string> asyncResult = new AsyncResult<string>(callback, this);
ThreadPool.QueueUserWorkItem(DoQuery, asyncResult);
return asyncResult;
}
public string EndQuery(IAsyncResult ar){
AsyncResult<string> asyncResult = (AsyncResult<string>)ar;
return asyncResult.EndInvoke();
}
}
然后我使用 AsyncEnumerator 并开始 (BeginQuery) 多个查询并在每个查询完成时处理结果(使用 yield return / EndQuery)。这似乎运作良好。但是在阅读了这么多 APM 已过时之后,我想知道如何使用 TAP 来做到这一点。另外,这种APM方法有什么问题吗?
谢谢!
一种更简单的方法来完成您正在寻找的事情是使用 Task
class 调用该方法。在您的情况下,它看起来像这样:
Task<string> getResultTask = Task.Run<string>(()=>GetResultFromWeb(myUrl));
// Do whatever I need to do that doesn't need the query result
string result = await getResultTask;
Process(result);
虽然这将像您的 IAsyncResult
选项那样创建另一个线程,但这大大简化了过程。
您的 API 是异步的,使用旧的 Begin/End 模型。这符合 TPL via
Task.Factory.FromAsync<string>(BeginQuery, EndQuery)
哪个returns一个Task<string>
哪个你可以await
。
what if we have an IO operation that happens in a method that is not declared as async?
在这种情况下,I/O 操作是阻塞的。换句话说,GetResultFromWeb
阻塞调用线程。在我们完成其余部分时请记住这一点...
I must use this method to do so.
据此我推断您不能编写异步的 GetResultFromWebAsync
方法。所以任何执行网络请求的线程必须被阻塞。
is there any way of performing these IO operations asynchronously without having to create one thread for each query?
最自然的做法是写一个GetResultFromWebAsync
方法。由于这是不可能的,您的选择是:阻止调用线程,或阻止其他线程(即线程池线程)。阻塞线程池线程是一种我称之为 "fake asynchrony" 的技术——因为它看起来是异步的(即不阻塞 UI 线程)但实际上不是(即它只是阻塞线程池线程)。
If I could, I'd like to create as less threads as possible, without having to block any of the threads.
鉴于限制,这是不可能的。如果您必须使用GetResultFromWeb
方法,而该方法阻塞调用线程,则线程必须 被屏蔽了。
One way I found to do so was by implementing APM, following this article from Jeffrey Richter and then, in my Begin method, I call ThreadPool.QueueWorkItem(GetResultFromWeb, asyncResult).
在这种情况下,您的代码公开了一个异步 API (begin/end),但在实现中它只是在线程池线程上调用 GetResultFromWeb
。即,它是伪异步。
This seems to work well.
它可以工作,但不是真正的异步。
But after reading so much that APM is obsolete, I was wondering how could I do this using TAP.
正如其他人所指出的,有一种更简单的方法可以将工作安排到线程池:Task.Run
。
真正的异步是不可能的,因为您必须使用 阻塞 方法。因此,您所能做的就是解决方法 - 假异步,a.k.a。阻塞线程池线程。最简单的方法是:
Task<string> getResultTask = Task.Run(() => GetResultFromWeb(myUrl));
// Do whatever I need to do that doesn't need the query result
string result = await getResultTask;
Process(result);
(比 APM 和 AsyncEnumerator 更简洁的代码)
请注意,我 不 建议创建一个 GetResultFromWebAsync
使用伪异步实现的方法。返回任务的异步后缀方法应该遵循 Task-based Asynchronous Pattern guidelines,这意味着 true 异步。
换句话说,正如我在我的博客上更详细地描述的那样,use Task.Run
to invoke a method, not to implement a method。