是否可以等待未声明为异步的IO操作?如果不是这样,我该怎么办? [英] Is it possible to await an IO operation that is not declared as async? If not, what should I do?

查看:125
本文介绍了是否可以等待未声明为异步的IO操作?如果不是这样,我该怎么办?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是新的C#中的异步编程,我仍然感到困惑的几件事情。
我读过.NET 4.5之后,APM和EAP不再推荐在新的发展,因为TAP应该取代他们(的来源)。

我想我知道如何异步/等待的作品,我会能够使用它们执行具有异步方法的IO操作。例如,我可以写,正等待一个HttpWebClient的GetStringAsync的结果,因为它的声明为异步方法异步方法。这是伟大的。

我的问题是:如果我们有这种情况发生在未声明为异步方法的IO操作?是这样的:假设我有一个有一个方法的API

 字符串GetResultFromWeb()

该查询从Web上的东西。而且我有很多不同的查询做,我的必须使用这个方法来做到这一点。然后我需要处理每一个查询结果。我明白我应该这样做,如果这是异步方法:

 任务<串GT; getResultTask = GetResultFromWeb(myUrl);
//做我需要做的,这并不需要查询结果
字符串结果=等待getResultTask;
流程(结果);

但因为它不是,我不能等待它 - 它告诉我字符串不是awaitable。所以我的问题是:是否有异步执行这些IO操作,而无需创建一个线程为每个查询的方法吗?如果我可以,我想创造尽可能少的线程成为可能,而不必阻止任何线程。

我发现这样做的一个方法是使通过实施APM,以下 从杰弗里里希特,然后在我的Begin方法,我称之为ThreadPool.QueueWorkItem(GetResultFromWeb,asyncResult)。像这样的:

 公共类A {
    私人无效DoQuery(AR对象){
        AsyncResult<串GT; asyncResult =(AsyncResult<串GT;)AR;
        字符串结果= GetResultFromWeb();
        asyncResult.SetAsCompleted(结果,FALSE);
    }    公众的IAsyncResult BeginQuery(的AsyncCallback){
        AsyncResult<串GT; asyncResult =新AsyncResult<串GT;(回调,这一点);
        ThreadPool.QueueUserWorkItem(DoQuery,asyncResult);
        返回asyncResult;
    }    公共字符串EndQuery(IAsyncResult的AR){
        AsyncResult<串GT; asyncResult =(AsyncResult<串GT;)AR;
        返回asyncResult.EndInvoke();
    }
}

然后我用一个AsyncEnumerator并开始(BeginQuery)多个查询,并完成(使用收益回报/ EndQuery)处理结果作为其中的每一个。这似乎运作良好。但看完这么多,APM已经过时后,我想知道我怎么能做到这一点使用TAP。此外,有没有这种做法APM什么问题?

谢谢!


解决方案

  

如果我们有这种情况发生在未声明为异步方法的IO操作?


在这种情况下,在I / O操作被阻塞。换句话说, GetResultFromWeb 阻塞调用线程的。记住这一点,因为我们通过休息...


  

我必须使用此方法来做到这一点。


通过这一点,我推断你不能写在 GetResultFromWebAsync 方法,它是异步的。因此,任何线程执行的Web请求的必须的被封锁。


  

有异步执行这些IO操作,而无需创建一个线程为每个查询的方法吗?


最自然的方法是编写一个 GetResultFromWebAsync 方法。由于这是不可能的,你的选项有:阻塞调用线程,或阻止其它线程(即线程池线程)。阻塞线程池线程是一种技术,我称之为假不同步 - 因为它似乎异步的(即不阻塞UI线程),但它真的不是(即,它只是阻止线程池线程代替)


  

如果我可以,我想创造尽可能少的线程成为可能,而不必阻止任何线程。


给定的约束那是不可能的。如果你的必须的使用 GetResultFromWeb 法,该方法的的调用线程,那么线程的必须的被封锁。


  

我发现这样做的一个方法是使通过实施APM,本文从杰弗里里希特以下,然后,在我的Begin方法,我称之为ThreadPool.QueueWorkItem(GetResultFromWeb,asyncResult)。


在这种情况下,您的code被暴露异步API(开始/结束),但在执行它只是调用 GetResultFromWeb 上一个线程池线程。即,它是假的不同步。


  

这似乎运作良好。


它的工作原理,但它不是真正的异步。


  

不过,看了那么多,APM已经过时后,我想知道我怎么能做到这一点使用TAP。


正如其他人所指出的,有安排的工作线程池一个更简单的方法: Task.Run

真不同步是不可能的,因为你有一个的拦截的方法,你必须使用。因此,所有你能做的就是一种变通方法 - 假异步,又称阻塞线程池线程。要做到这一点最简单的方法是:

 任务<串GT; getResultTask = Task.Run(()=> GetResultFromWeb(myUrl));
//做我需要做的,这并不需要查询结果
字符串结果=等待getResultTask;
流程(结果);

(干净多了code比APM和AsyncEnumerator)

请注意,我做的的建议创建一个使用假异步实施 GetResultFromWebAsync 方法。任务返回,将异步后缀的方法都应该遵循基于任务的异步模式,这意味着准则的真正的不同步。

在换句话说,我更详细地描述我的博客上,的使用 Task.Run 来调用一个方法,不落实的方法

I'm new to asynchronous programming in C# and I'm still confused about a few things. I've read that after .NET 4.5, the APM and EAP are no longer recommended for new development since the TAP is supposed to replace them (source).

I think I understood how async/await works and I'd be able to use them for performing IO operations that have async methods. For example, I could write an async method that awaits for an HttpWebClient's GetStringAsync result, since it's declared as an async method. That's great.

My question is: what if we have an IO operation that happens in a method that is not declared as async? Like this: suppose I have an API that has a method

string GetResultFromWeb()

which queries something from the Web. And I have lots of different queries to do and I must use this method to do so. And then I need to process each query result. I understand that I'd do this if that was an async method:

Task<string> getResultTask = GetResultFromWeb(myUrl); 
// Do whatever I need to do that doesn't need the query result
string result = await getResultTask;
Process(result);

But since it's not, I cannot await for it -- it tells me string is not awaitable. So my question is: is there any way of performing these IO operations asynchronously without having to create one thread for each query? If I could, I'd like to create as less threads as possible, without having to block any of the threads.

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). Like this:

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

Then I use an AsyncEnumerator and begin (BeginQuery) several queries and process the results as each one of them finishes (using yield return / EndQuery). This seems to work well. But after reading so much that APM is obsolete, I was wondering how could I do this using TAP. Also, is there any problem with this APM approach?

Thanks!

解决方案

what if we have an IO operation that happens in a method that is not declared as async?

In this case, the I/O operation is blocking. In other words, GetResultFromWeb blocks the calling thread. Keep that in mind as we go through the rest...

I must use this method to do so.

By this I infer that you cannot write a GetResultFromWebAsync method which is asynchronous. So any thread doing the web requests must be blocked.

is there any way of performing these IO operations asynchronously without having to create one thread for each query?

The most natural approach is to write a GetResultFromWebAsync method. Since that isn't possible, your options are: block the calling thread, or block some other thread (i.e., a thread pool thread). Blocking a thread pool thread is a technique I call "fake asynchrony" - since it appears asynchronous (i.e., not blocking the UI thread) but it's really not (i.e., it just blocks a thread pool thread instead).

If I could, I'd like to create as less threads as possible, without having to block any of the threads.

That's not possible given the constraints. If you must use the GetResultFromWeb method, and that method blocks the calling thread, then a thread must be blocked.

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).

In this case, your code is exposing an asynchronous API (begin/end), but in the implementation it's just calling GetResultFromWeb on a thread pool thread. I.e., it is fake asynchrony.

This seems to work well.

It works, but it's not truly asynchronous.

But after reading so much that APM is obsolete, I was wondering how could I do this using TAP.

As others have noted, there's a much easier way to schedule work to the thread pool: Task.Run.

True asynchrony isn't possible, because you have a blocking method that you must use. So, all you can do is a workaround - fake asynchrony, a.k.a. blocking a thread pool thread. The easiest way to do that is:

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

(much cleaner code than APM and AsyncEnumerator)

Note that I do not recommend creating a GetResultFromWebAsync method that is implemented using fake asynchrony. The Task-returning, Async-suffix methods are supposed to follow the Task-based Asynchronous Pattern guidelines, which imply true asynchrony.

In other words, as I describe in more detail on my blog, use Task.Run to invoke a method, not to implement a method.

这篇关于是否可以等待未声明为异步的IO操作?如果不是这样,我该怎么办?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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