views:

430

answers:

5
+3  A: 

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();
    }
});
Cristian Libardo
+2  A: 

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.

Bob
+7  A: 

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:

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

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.

Greg Beech
+1  A: 

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 semaphore with the Semaphore class to count the number of running async jobs.

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.

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?

Could you share some bit of MyClass implementation? What it's supposed to do?

chakrit
Sleeping in a loop is considered bad practice. You should be using an EventWaitHandle to fire off an event upon finishing.
Nicholas Mancuso
Unfortunately, I'm limited to .NET CF 1.0, and the class Semaphore is only supported since .NET 2.0.
Auron
It's just an "example"... mind you.
chakrit
A: 

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:

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 pendingTasks variable concurrently, so the lock keyword should be used to prevent race conditions:

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 ObjectDisposedException when DoSomething() is called outside the using clause. But I don't find this bad enough to rethink this solution.

Auron
In the using (MyClass myclass = new....) example I think the myclass object becomes unanchored at the end of the using block and available to be Finalized. I'm pretty sure to be safe you need to call and block on a Waiting method before the end of the using block.
Rob McCready