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 expo开发者_如何学编程se the IAsyncResult, or is there something wrong with this that I'm not thinking of.
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.
IMHO, I don't think your async method add any value, so just let the client decide whether it will call your method asynchronously.
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.
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;
}
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.
精彩评论