C#中的Typesafe即发即弃异步委托调用

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)

dtb*_*dtb 8

您可以将EndInvoke作为AsyncCallback传递给BeginInvoke:

Action<byte[], int, int> action = // ...

action.BeginInvoke(buffer, 0, buffer.Length, action.EndInvoke, null);
Run Code Online (Sandbox Code Playgroud)

这有帮助吗?


Sco*_*ein 5

怎么样的:

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在这里可能是最好的.


Aar*_*ght 5

我注意到没有人对此作出回应:

我也无法在.NET线程池上运行代码,因为它可能需要很长时间才能完成(建议只在线程池上运行相对短暂的代码) - 这使得无法使用ThreadPool. QueueUserWorkItem().

我不确定你是否意识到这一点,但异步代理实际上正是这样做的 - 他们将工作排在工作线程中ThreadPool,就像你做的那样QueueUserWorkItem.

异步委托表现不同的唯一时间是它们是特殊的框架委托,如Stream.BeginReadSocket.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)

(注意以上也会使用线程池!)