是否有可能等待未声明为异步的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应该取代它们( 源代码 )。
我想我理解异步/等待如何工作,我可以使用它们来执行具有异步方法的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的这篇文章 ,然后在我的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方法有什么问题吗?
谢谢!
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!
原文:https://stackoverflow.com/questions/27790468
最满意答案
编译器通常不知道A的构造函数是否有可观察的副作用,所以它总是会调用它。 它可能不会保持变量'a'。
因此,将调用构造函数,但结果可能不会分配给变量; 相反,如果没有其他任何东西引用它,A对象可能只是立即注册垃圾收集。 (在你的情况下,别的东西会引用它 - 即Container类 - 所以它不会被垃圾收集!)
在您的情况下,构造函数显然在任何情况下都有副作用(因此编译器优化掉构造函数调用将是一个重大错误)。
综上所述:
- 构造函数将始终被调用。
- 将结果赋值给局部变量可能无法完成,因为编译器知道它没有可观察的副作用。
- 在你的代码中,其他东西保留了对构造对象的引用,所以它不会被GCed。
The compiler doesn't in general know if the constructor of A has observable side-effects, so it will always call it. It may not keep the variable 'a' around though.
So, the constructor will be called, but the result may not be assigned to the variable; instead the A object may just be immediately registered for garbage collection if nothing else references it. (In your case, something else DOES reference it - namely, the Container class - so it WON'T be garbage-collected!)
In your case, the constructor manifestly DOES have side-effects in any case (so it would be a major error for the compiler to optimise away the constructor call).
In summary:
- The constructor will always be called.
- The assignment of the result to the local variable may not be done because the compiler knows that it has no observable side-effects.
- In your code, something else retains a reference to the constructed object, so it won't be GCed.
相关问答
更多-
我会避免宣布它是一个global 。 你可以在函数中声明它: cmd = None if transfer_type == 'download': cmd = 'scp -qr %s:%s %s' % (host, remote, local) if transfer_type == 'upload': cmd = 'scp -qr %s %s:%s' % (local, host, remote) 当您尝试运行cmd时,这仍可能导致问题。 相反,如果transfer_type与任何内容都 ...
-
不能使用global语句修改全局变量: def fun(): global a while (c > a/b): a = a + 1 print a/b 只要python看到类似a = a + 1的赋值语句,它就会认为变量a是局部变量,当函数被调用时,表达式c > a/b会引发错误,因为a尚未定义。 You can't modify a global variable without using the global statement: def fun( ...
-
UnboundLocalError:在赋值之前引用的局部变量....(UnboundLocalError: local variable … referenced before assignment)[2023-12-03]
刚切换: class Test(webapp.RequestHandler): def err_user_not_found(self): self.redirect(users.create_login_url(self.request.uri)) def get(self): user = users.get_current_user() # error path if not user: ... -
如果是setValue(&val); 通过指针deference将val赋值给某些东西,然后定义代码的行为,并且分析工具不正确。 但是如果你可以避免变量处于未初始化状态(没有多余的任务),那么这将是更可取的。 是否有可能重构 int val = setValue(); 说? If setValue(&val); does assign val to something via a pointer deference, then the behaviour of your code is defined, ...
-
即使变量未被使用,局部变量赋值是否也会被执行?(Will local variable assignment always be executed, even if variable is unused?)[2023-08-31]
编译器通常不知道A的构造函数是否有可观察的副作用,所以它总是会调用它。 它可能不会保持变量'a'。 因此,将调用构造函数,但结果可能不会分配给变量; 相反,如果没有其他任何东西引用它,A对象可能只是立即注册垃圾收集。 (在你的情况下,别的东西会引用它 - 即Container类 - 所以它不会被垃圾收集!) 在您的情况下,构造函数显然在任何情况下都有副作用(因此编译器优化掉构造函数调用将是一个重大错误)。 综上所述: 构造函数将始终被调用。 将结果赋值给局部变量可能无法完成,因为编译器知道它没有可观察的副作 ... -
Python |(Python | if variable: | UnboundLocalError: local variable 'variable' referenced before assignment)[2023-08-03]
在函数的开头初始化变量为None。 然后,您可以使用以下方式查看它: if x is not None: At the beginning of the function initialize the variable to None. Then later you can check it with: if x is not None: -
你正在进行模数除法,你的运行变量从4. 4 % 2 == 0 , x将被定义,而4 % 3 != 0且y未定义,但在下一行中使用。 You are doing a modulo division and your running variable starts at 4. 4 % 2 == 0 and x will be defined, while 4 % 3 != 0 and y is not defined, but used in the next line.
-
它因为失败而失败 del image 线。 del运算符没有按照您的想法执行。 看看这个: >>> import dis >>> def test(): ... x = 1 ... del x ... >>> dis.dis(test) 2 0 LOAD_CONST 1 (1) 3 STORE_FAST 0 (x) 3 6 DELETE_FAST ...
-
在__init__函数内部,没有NumRid ,但是你试图增加它。 它应该是self.NumRid += 1如果它是一个实例变量,或者Person.NumRid += 1如果它是一个类变量(或者,对于将来证明重命名类: self.__class__.NumRid += 1 )。 Inside the __init__ function, there is no NumRid, and yet you are trying to increment it. It should be either self.N ...
-
将T声明为全局变量: def do_something(): global T # <-------------- do_something_else(T) # err at this line T += 1 Declare T as global variable: def do_something(): global T # <-------------- do_something_else(T) # err at this line T += 1