views:

243

answers:

5

I've been on a tear lately trying to learn everything I can about .Net Multithreading. (Getting better at it, but still feel like there's lots to learn). Right now I'm focusing on the APM (Asynchronous Programming Model) which is commonly known as this:

//non async method
public int DoSomeWork(int arg)
{
   //do something and return result
}

//async version
public IAsyncResult BeginDoSomeWork(int arg, AsyncCallback callback, object @object)
{

}

public int EndDoSomeWork(IAsyncResult result)
{

}

Now, let's say I'm writing some library, and I want to expose this functionality to anyone who's consuming my API, I was thinking about ways to implement this pattern. Implementing the IAsyncResult interface is a possibility, but it seems quite complex. My question is, if using delegate is an acceptable solution. What I mean by that is this:

public class MyClass
{
    private Func<int, int> func;

    //non async method
    public int DoSomeWork(int arg)
    {
        //do something and return result
    }

    //async version
    public IAsyncResult BeginDoSomeWork(int arg, AsyncCallback callback, object @object)
    {
        this.func = new Func<int, int>(DoSomeWork);
        var asyncResult = this.func.BeginInvoke(arg,callback,object);
        return asyncResult;
    }

    public int EndDoSomeWork(IAsyncResult result)
    {
        return this.func.EndInvoke(result);
    }
}

Basically, every delegate has the BeginXxx and EndXxx functionality baked right into it. Is it OK to take advantage of that and just expose the IAsyncResult, or is there something wrong with this that I'm not thinking of.

A: 

IMHO, I don't think your async method add any value, so just let the client decide whether it will call your method asynchronously.

Benny
It was just an example. Ignore the actual implementation (which admittedly looks stupid and pointless), I'm asking about using the delegate and it's IAsyncResult.
BFree
Because both a synchronous and asynchronous version is provided the client still gets to make that decision. The OP is more concerned about whether the method chosen is a good way to implement APM or not.
Crippledsmurf
@BFree, i c now.
Benny
-1 no one can make the assumption that the async method will or will not provide any added value to the OPs hypothetical library.
dboarman
A: 

I think it is a very valid exercise if nothing else. I, too, am working on an asynchronous 'delegate' handler. You can increase the flexibility, and you will learn a great deal about the async model.

I know this SO question contains some Linq exercise in it. However, it might give you an idea on using expression trees to make it a bit more robust. I have yet to dive into the subject and provide you with more concrete information.

Here is a sample of a piece of my old code on publishing asynchronous methods. This is a dated exercise in learning reflection and some interesting implementations. Take it for what it's worth, but it might help you with some ideas:

public delegate void delVoidMethod(params object[] args);

/// <summary>
/// Publishes an asynchronous method to the delegate collection.
/// </summary>
/// <param name="methodOwner">Target object owning the delegated method.</param>
/// <param name="method">The delegated method.</param>
/// <param name="callback">The method designated as a callback delegate.</param>
/// <param name="ptr">The delegated method's runtime handle.</param>
/// <returns>True if publishing was successful.</returns>
public bool PublishAsyncMethod(object target , MethodInfo method ,
    MethodInfo callback , out IntPtr ptr)
{
    try
    {
        ptr = method.MethodHandle.Value;

        delVoidMethod dMethod = (delVoidMethod)Delegate.CreateDelegate
            (typeof(delVoidMethod) , target , method);
        AsyncCallback callBack = (AsyncCallback)Delegate.CreateDelegate
             (typeof(AsyncCallback) , target , callback);

        handlers[ptr] = new DelegateStruct(dMethod , callBack);

        Logger.WriteLine("Delegate : {0}.{1} -> {2}.{3} published." ,
            method.DeclaringType.Name , method.Name ,
            callback.DeclaringType.Name , callback.Name);
        return true;
    }
    catch (ArgumentException ArgEx)
    {
        Logger.Write(DH_ERROR , ERR_MSG ,
            ArgEx.Source , ArgEx.InnerException , ArgEx.Message);
    }
    catch (MissingMethodException BadMethEx)
    {
        Logger.Write(DH_ERROR , ERR_MSG ,
            BadMethEx.Source , BadMethEx.InnerException , BadMethEx.Message);
    }
    catch (MethodAccessException MethAccEx)
    {
        Logger.Write(DH_ERROR , ERR_MSG ,
            MethAccEx.Source , MethAccEx.InnerException , MethAccEx.Message);
    }

    ptr = IntPtr.Zero;

    return false;
}
dboarman
A: 

Absolutely nothing wrong with it, but it seems a bit pointless.

You're essentially implementing the Facade pattern, but not making the interface any simpler. It might be better (depending on your specific situation) to add your own classes to simplify the use AsyncCallback and IAsyncResult to make the calls more accurately reflect the properties used in your classes.

David_001
I don't see the similarities in what the OP is doing compared to the bullets defining the Facade pattern.
dboarman
+1  A: 

I think that's a good way to implement the APM, but at the moment you'd get an error with more than one asynchronous call per instance.

Why don't you just externalise the delegate usage?

What I mean is, than if you just put the actual logic in your class, but then let anyone who calls your method do:

MyClass c = new MyClass();
Func<int, int> f = c.DoSomeWork;
IAsyncResult ar1 = f.BeginInvoke(1, null, null);
IAsyncResult ar2 = f.BeginInvoke(2, null, null);
//...

I guess what i'm advocating is not even implementing the APM yourself, but just recommending to people that call your method that they use the APM that's built into delegates.

Rob Fonseca-Ensor
A: 

I personally prefer using an event to notify when the asynchronous process end if the class is intended to be used by another library, using the AsyncOperation class and the SendOrPostCallback delegate to make sure the events are raised on the callers thread to not disrupt the UI. If however the asynchronous operation is to be executed in the same assembly, I rather prefer the calling code to define how to make the asynchronous call.

Wilhelm