LBu*_*kin 13 c# generics delegates asynchronous .net-4.0
我最近发现自己需要一种类型安全的"即发即忘"机制来异步运行代码.
理想情况下,我想要做的是:
var myAction = (Action)(() => Console.WriteLine("yada yada"));
myAction.FireAndForget(); // async invocation
Run Code Online (Sandbox Code Playgroud)
不幸的是,BeginInvoke()没有相应的调用的明显选择EndInvoke()不起作用 - 它导致资源泄漏缓慢(因为asyn状态由运行时保持并且从未释放...它期望最终调用EndInvoke().我也无法运行.NET线程池上的代码,因为它可能需要很长时间才能完成(建议只在线程池上运行相对短暂的代码) - 这使得无法使用ThreadPool.QueueUserWorkItem().
起初,我只需要这一行为,其签名匹配方法Action,Action<...>或Func<...>.所以我把一组扩展方法(见下面的清单)放在一起,让我在不遇到资源泄漏的情况下这样做.每个版本的Action/Func都有重载.
不幸的是,我现在想将此代码移植到.NET 4,其中Action和Func上的泛型参数的数量已大幅增加.在我编写T4脚本来生成这些之前,我还希望找到一种更简单,更优雅的方法来实现这一点.欢迎任何想法.
public static class AsyncExt
{
public static void FireAndForget( this Action action )
{
action.BeginInvoke(OnActionCompleted, action);
}
public static void FireAndForget<T1>( this Action<T1> action, T1 arg1 )
{
action.BeginInvoke(arg1, OnActionCompleted<T1>, action);
}
public static void FireAndForget<T1,T2>( this Action<T1,T2> action, T1 arg1, T2 arg2 )
{
action.BeginInvoke(arg1, arg2, OnActionCompleted<T1, T2>, action);
}
public static void FireAndForget<TResult>(this Func<TResult> func, TResult arg1)
{
func.BeginInvoke(OnFuncCompleted<TResult>, func);
}
public static void FireAndForget<T1,TResult>(this Func<T1, TResult> action, T1 arg1)
{
action.BeginInvoke(arg1, OnFuncCompleted<T1,TResult>, action);
}
// more overloads of FireAndForget<..>() for Action<..> and Func<..>
private static void OnActionCompleted( IAsyncResult result )
{
var action = (Action)result.AsyncState;
action.EndInvoke(result);
}
private static void OnActionCompleted<T1>( IAsyncResult result )
{
var action = (Action<T1>)result.AsyncState;
action.EndInvoke( result );
}
private static void OnActionCompleted<T1,T2>(IAsyncResult result)
{
var action = (Action<T1,T2>)result.AsyncState;
action.EndInvoke(result);
}
private static void OnFuncCompleted<TResult>( IAsyncResult result )
{
var func = (Func<TResult>)result.AsyncState;
func.EndInvoke( result );
}
private static void OnFuncCompleted<T1,TResult>(IAsyncResult result)
{
var func = (Func<T1, TResult>)result.AsyncState;
func.EndInvoke(result);
}
// more overloads of OnActionCompleted<> and OnFuncCompleted<>
}
Run Code Online (Sandbox Code Playgroud)
您可以将EndInvoke作为AsyncCallback传递给BeginInvoke:
Action<byte[], int, int> action = // ...
action.BeginInvoke(buffer, 0, buffer.Length, action.EndInvoke, null);
Run Code Online (Sandbox Code Playgroud)
这有帮助吗?
怎么样的:
public static class FireAndForgetMethods
{
public static void FireAndForget<T>(this Action<T> act,T arg1)
{
var tsk = Task.Factory.StartNew( ()=> act(arg1),
TaskCreationOptions.LongRunning);
}
}
Run Code Online (Sandbox Code Playgroud)
使用它像:
Action<int> foo = (t) => { Thread.Sleep(t); };
foo.FireAndForget(100);
Run Code Online (Sandbox Code Playgroud)
要添加类型安全性,只需展开辅助方法即可.T4在这里可能是最好的.
我注意到没有人对此作出回应:
我也无法在.NET线程池上运行代码,因为它可能需要很长时间才能完成(建议只在线程池上运行相对短暂的代码) - 这使得无法使用ThreadPool. QueueUserWorkItem().
我不确定你是否意识到这一点,但异步代理实际上正是这样做的 - 他们将工作排在工作线程中ThreadPool,就像你做的那样QueueUserWorkItem.
异步委托表现不同的唯一时间是它们是特殊的框架委托,如Stream.BeginRead或Socket.BeginSend.这些使用I/O完成端口.
除非你在ASP.NET环境中旋转数百个这样的任务,否则我建议只使用线程池.
ThreadPool.QueueUserWorkItem(s => action());
Run Code Online (Sandbox Code Playgroud)
或者,在.NET 4中,您可以使用任务工厂:
Task.Factory.StartNew(action);
Run Code Online (Sandbox Code Playgroud)
(注意以上也会使用线程池!)