C# 如何异步处理?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/400130/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to dispose asynchronously?
提问by Auron
Let's say I have a class that implements the IDisposableinterface. Something like this:
假设我有一个实现IDisposable接口的类。像这样的东西:
MyClassuses some unmanaged resources, hence the Dispose()method from IDisposablereleases those resources. MyClassshould be used like this:
MyClass使用一些非托管资源,因此IDisposable的Dispose()方法释放这些资源。MyClass应该像这样使用:
using ( MyClass myClass = new MyClass() ) {
myClass.DoSomething();
}
Now, I want to implement a method that calls DoSomething()asynchronously. I add a new method to MyClass:
现在,我想实现一个异步调用DoSomething()的方法。我向MyClass添加了一个新方法:
Now, from the client side, MyClassshould be used like this:
现在,从客户端,MyClass应该像这样使用:
using ( MyClass myClass = new MyClass() ) {
myClass.AsyncDoSomething();
}
However, if I don't do anything else, this could fail as the object myClassmight be disposed before DoSomething()is called (and throw an unexpected ObjectDisposedException). So, the call to the Dispose()method (either implicit or explicit) should be delayed until the asynchronous call to DoSomething()is done.
但是,如果我不做任何其他事情,这可能会失败,因为对象myClass可能在调用DoSomething()之前被处理(并抛出意外的ObjectDisposedException)。因此,应该延迟对Dispose()方法(隐式或显式)的调用,直到对DoSomething()的异步调用完成。
I think the code in the Dispose()method should be executed in a asynchronous way, and only once all asynchronous calls are resolved. I'd like to know which could be the best way to accomplish this.
我认为Dispose()方法中的代码应该以异步方式执行,并且只有在所有异步调用都解决之后。我想知道哪种方法是实现这一目标的最佳方法。
Thanks.
谢谢。
NOTE: For the sake of simplicity, I haven't entered in the details of how Dispose() method is implemented. In real life I usually follow the Dispose pattern.
注意:为了简单起见,我没有详细介绍 Dispose() 方法是如何实现的。在现实生活中,我通常遵循Dispose 模式。
UPDATE:Thank you so much for your responses. I appreciate your effort. As chakrithas commented, I need that multiple calls to the async DoSomething can be made. Ideally, something like this should work fine:
更新:非常感谢您的回复。我很欣赏你的努力。正如chakrit所评论的,我需要对异步 DoSomething 进行多次调用。理想情况下,这样的事情应该可以正常工作:
using ( MyClass myClass = new MyClass() ) {
myClass.AsyncDoSomething();
myClass.AsyncDoSomething();
}
I'll study the counting semaphore, it seems what I'm looking for. It could also be a design problem. If I find it convenient, I will share with you some bits of the real case and what MyClassreally does.
我会研究计数信号量,这似乎是我要找的。也可能是设计问题。如果我觉得方便,我将与您分享一些真实案例以及MyClass 的真正作用。
采纳答案by Auron
So, my idea is to keep how many AsyncDoSomething()are pending to complete, and only dispose when this count reaches to zero. My initial approach is:
因此,我的想法是保留有多少AsyncDoSomething()待完成,并且仅在此计数达到零时进行处理。我最初的做法是:
public class MyClass : IDisposable {
private delegate void AsyncDoSomethingCaller();
private delegate void AsyncDoDisposeCaller();
private int pendingTasks = 0;
public DoSomething() {
// Do whatever.
}
public AsyncDoSomething() {
pendingTasks++;
AsyncDoSomethingCaller caller = new AsyncDoSomethingCaller();
caller.BeginInvoke( new AsyncCallback( EndDoSomethingCallback ), caller);
}
public Dispose() {
AsyncDoDisposeCaller caller = new AsyncDoDisposeCaller();
caller.BeginInvoke( new AsyncCallback( EndDoDisposeCallback ), caller);
}
private DoDispose() {
WaitForPendingTasks();
// Finally, dispose whatever managed and unmanaged resources.
}
private void WaitForPendingTasks() {
while ( true ) {
// Check if there is a pending task.
if ( pendingTasks == 0 ) {
return;
}
// Allow other threads to execute.
Thread.Sleep( 0 );
}
}
private void EndDoSomethingCallback( IAsyncResult ar ) {
AsyncDoSomethingCaller caller = (AsyncDoSomethingCaller) ar.AsyncState;
caller.EndInvoke( ar );
pendingTasks--;
}
private void EndDoDisposeCallback( IAsyncResult ar ) {
AsyncDoDisposeCaller caller = (AsyncDoDisposeCaller) ar.AsyncState;
caller.EndInvoke( ar );
}
}
Some issues may occur if two or more threads try to read / write the pendingTasksvariable concurrently, so the lockkeyword should be used to prevent race conditions:
如果两个或多个线程同时尝试读取/写入pendingTasks变量,则可能会出现一些问题,因此应使用lock关键字来防止竞争条件:
public class MyClass : IDisposable {
private delegate void AsyncDoSomethingCaller();
private delegate void AsyncDoDisposeCaller();
private int pendingTasks = 0;
private readonly object lockObj = new object();
public DoSomething() {
// Do whatever.
}
public AsyncDoSomething() {
lock ( lockObj ) {
pendingTasks++;
AsyncDoSomethingCaller caller = new AsyncDoSomethingCaller();
caller.BeginInvoke( new AsyncCallback( EndDoSomethingCallback ), caller);
}
}
public Dispose() {
AsyncDoDisposeCaller caller = new AsyncDoDisposeCaller();
caller.BeginInvoke( new AsyncCallback( EndDoDisposeCallback ), caller);
}
private DoDispose() {
WaitForPendingTasks();
// Finally, dispose whatever managed and unmanaged resources.
}
private void WaitForPendingTasks() {
while ( true ) {
// Check if there is a pending task.
lock ( lockObj ) {
if ( pendingTasks == 0 ) {
return;
}
}
// Allow other threads to execute.
Thread.Sleep( 0 );
}
}
private void EndDoSomethingCallback( IAsyncResult ar ) {
lock ( lockObj ) {
AsyncDoSomethingCaller caller = (AsyncDoSomethingCaller) ar.AsyncState;
caller.EndInvoke( ar );
pendingTasks--;
}
}
private void EndDoDisposeCallback( IAsyncResult ar ) {
AsyncDoDisposeCaller caller = (AsyncDoDisposeCaller) ar.AsyncState;
caller.EndInvoke( ar );
}
}
I see a problem with this approach. As the release of resources is asynchronously done, something like this might work:
我发现这种方法有问题。由于资源的释放是异步完成的,这样的事情可能会起作用:
MyClass myClass;
using ( myClass = new MyClass() ) {
myClass.AsyncDoSomething();
}
myClass.DoSomething();
When the expected behavior should be to launch an ObjectDisposedExceptionwhen DoSomething()is called outside the usingclause. But I don't find this bad enough to rethink this solution.
当在using子句之外调用DoSomething()时,预期行为应该是启动ObjectDisposedException时。但我认为这还不足以重新考虑这个解决方案。
回答by Cristian Libardo
Async methods usually have a callback allowing you to do do some action upon completition. If this is your case it would be something like this:
异步方法通常有一个回调,允许你在完成时做一些动作。如果这是你的情况,它会是这样的:
// The async method taks an on-completed callback delegate
myClass.AsyncDoSomething(delegate { myClass.Dispose(); });
An other way around this is an async wrapper:
另一种解决方法是异步包装器:
ThreadPool.QueueUserWorkItem(delegate
{
using(myClass)
{
// The class doesn't know about async operations, a helper method does that
myClass.DoSomething();
}
});
回答by Bob
I wouldn't alter the code somehow to allow for async disposes. Instead I would make sure when the call to AsyncDoSomething is made, it will have a copy of all the data it needs to execute. That method should be responsible for cleaning up all if its resources.
我不会以某种方式更改代码以允许异步处理。相反,我会确保在调用 AsyncDoSomething 时,它将拥有它需要执行的所有数据的副本。那个方法应该负责清理所有如果它的资源。
回答by Greg Beech
It looks like you're using the event-based async pattern (see here for more info about .NET async patterns) so what you'd typically have is an event on the class that fires when the async operation is completed named DoSomethingCompleted
(note that AsyncDoSomething
should really be called DoSomethingAsync
to follow the pattern correctly). With this event exposed you could write:
看起来您正在使用基于事件的异步模式(有关 .NET 异步模式的更多信息,请参见此处),因此您通常拥有的是在异步操作完成时触发的类上的事件命名DoSomethingCompleted
(注意AsyncDoSomething
应该真正被调用DoSomethingAsync
以正确遵循模式)。暴露此事件后,您可以编写:
var myClass = new MyClass();
myClass.DoSomethingCompleted += (sender, e) => myClass.Dispose();
myClass.DoSomethingAsync();
The other alternative is to use the IAsyncResult
pattern, where you can pass a delegate that calls the dispose method to the AsyncCallback
parameter (more info on this pattern is in the page above too). In this case you'd have BeginDoSomething
and EndDoSomething
methods instead of DoSomethingAsync
, and would call it something like...
另一种选择是使用IAsyncResult
模式,您可以将调用 dispose 方法的委托传递给AsyncCallback
参数(有关此模式的更多信息也在上面的页面中)。在这种情况下,您将拥有BeginDoSomething
andEndDoSomething
方法而不是DoSomethingAsync
, 并将其称为...
var myClass = new MyClass();
myClass.BeginDoSomething(
asyncResult => {
using (myClass)
{
myClass.EndDoSomething(asyncResult);
}
},
null);
But whichever way you do it, you need a way for the caller to be notified that the async operation has completed so it can dispose of the object at the correct time.
但是无论您采用哪种方式,您都需要一种方法来通知调用者异步操作已完成,以便它可以在正确的时间处理对象。
回答by chakrit
You could add a callback mechanism and pass a cleanup function as a callback.
您可以添加回调机制并将清理函数作为回调传递。
var x = new MyClass();
Action cleanup = () => x.Dispose();
x.DoSomethingAsync(/*and then*/cleanup);
but this would pose problem if you want to run multiple async calls off the same object instance.
但是如果你想在同一个对象实例上运行多个异步调用,这会带来问题。
One way would be to implement a simple counting semaphorewith the Semaphore classto count the number of running async jobs.
一种方法是使用Semaphore 类实现一个简单的计数信号量来计算正在运行的异步作业的数量。
Add the counter to MyClass and on every AsyncWhatever calls increment the counter, on exits decerement it. When the semaphore is 0, then the class is ready to be disposed.
将计数器添加到 MyClass 并在每个 AsyncWhatever 调用上递增计数器,退出时递减它。当信号量为 0 时,该类已准备好被处理。
var x = new MyClass();
x.DoSomethingAsync();
x.DoSomethingAsync2();
while (x.RunningJobsCount > 0)
Thread.CurrentThread.Sleep(500);
x.Dispose();
But I doubt that would be the ideal way. I smell a design problem. Maybe a re-thought of MyClass designs could avoid this?
但我怀疑这将是理想的方式。我闻到了设计问题。也许重新考虑 MyClass 设计可以避免这种情况?
Could you share some bit of MyClass implementation? What it's supposed to do?
你能分享一些 MyClass 实现吗?它应该做什么?
回答by supercat
I consider it unfortunate that Microsoft didn't require as part of the IDisposable
contract that implementations should allow Dispose
to be called from any threading context, since there's no sane way the creation of an object can force the continued existence of the threading context in which it was created. It's possible to design code so that the thread which creates an object will somehow watch for the object becoming obsolete and can Dispose
at its convenience, and so that when the thread is no longer needed for anything else it will stick around until all appropriate objects have been Dispose
d, but I don't think there's a standard mechanism that doesn't require special behavior on the part of the thread creating the Dispose
.
我认为不幸的是,微软没有要求作为IDisposable
合同的一部分,实现应该允许Dispose
从任何线程上下文调用,因为没有理智的方式创建对象可以强制继续存在它所在的线程上下文创建。可以设计代码,以便创建对象的线程以某种方式监视对象变得过时并可以Dispose
在其方便时,并且当线程不再需要其他任何东西时,它会一直存在,直到所有合适的对象都已Dispose
d,但我认为没有一种标准机制不需要创建Dispose
.
Your best bet is probably to have all the objects of interest created within a common thread (perhaps the UI thread), try to guarantee that the thread will stay around for the lifetime of the objects of interest, and use something like Control.BeginInvoke
to request the objects' disposal. Provided that neither object creation nor cleanup will block for any length of time, that may be a good approach, but if either operation could block a different approach may be needed [perhaps open up a hidden dummy form with its own thread, so one can use Control.BeginInvoke
there].
最好的办法可能是在一个公共线程(可能是 UI 线程)中创建所有感兴趣的对象,尽量保证该线程在感兴趣的对象的生命周期内保持不变,并使用类似的东西Control.BeginInvoke
来请求对象' 处理。假设对象创建和清理都不会阻塞任何时间长度,这可能是一种好方法,但是如果任一操作都可以阻塞,则可能需要不同的方法[也许用自己的线程打开一个隐藏的虚拟表单,这样就可以Control.BeginInvoke
在那里使用]。
Alternatively, if you have control over the IDisposable
implementations, design them so that they can safely be fired asynchronously. In many cases, that will "just work" provided nobody is trying to use the item when it is disposed, but that's hardly a given. In particular, with many types of IDisposable
, there's a real danger that multiple object instances might both manipulate a common outside resource [e.g. an object may hold a List<>
of created instances, add instances to that list when they are constructed, and remove instances on Dispose
; if the list operations are not synchronized, an asynchronous Dispose
could corrupt the list even if the object being disposedis not otherwise in use.
或者,如果您可以控制IDisposable
实现,请将它们设计为可以安全地异步触发。在许多情况下,如果没有人在处理该物品时试图使用该物品,这将“正常工作”,但这几乎不是给定的。特别是,对于许多类型的IDisposable
,存在多个对象实例可能同时操作公共外部资源的真正危险[例如,一个对象可能持有一个List<>
已创建的实例,在构造它们时将实例添加到该列表中,并在 上删除实例Dispose
;如果列表操作不同步,Dispose
即使正在处理的对象没有以其他方式使用,异步操作也可能破坏列表。
BTW, a useful pattern is for objects to allow asynchronous dispose while they are in use, with the expectation that such disposal will cause any operations in progress to throw an exception at the first convenient opportunity. Things like sockets work this way. It may not be possible for a read operation to be exit early without leaving its socket in a useless state, but if the socket's never going to be used anyway, there's no point for the read to keep waiting for data if another thread has determined that it should give up. IMHO, that's how all IDisposable
objects should endeavor to behave, but I know of no document calling for such a general pattern.
顺便说一句,一个有用的模式是让对象在使用时允许异步处理,期望这种处理会导致任何正在进行的操作在第一个方便的机会抛出异常。像套接字这样的东西就是这样工作的。读取操作可能无法提前退出而不使其套接字处于无用状态,但是如果无论如何都不会使用套接字,那么如果另一个线程已经确定,读取就没有必要继续等待数据它应该放弃。恕我直言,这就是所有IDisposable
对象都应该努力表现的方式,但我知道没有文件要求这种通用模式。