Недавно я обнаружил, что мне нужен безопасный для безопасности механизм «запускай и забывай» для асинхронного выполнения кода.
В идеале я хотел бы сделать что-то вроде:
var myAction = (Action)(() => Console.WriteLine("yada yada"));
myAction.FireAndForget(); // async invocation
К сожалению, очевидный выбор вызова BeginInvoke()
без соответствующего EndInvoke()
не работает - это приводит к медленной утечке ресурсов (поскольку асинхронное состояние сохраняется во время выполнения и никогда не освобождается ... оно ожидает возможного вызов 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<>
}