Gần đây tôi thấy mình cần một cơ chế "lửa và quên" an toàn để chạy mã không đồng bộ.Cuộc gọi ủy nhiệm không đồng bộ về lửa và không đồng bộ trong C#
Lý tưởng nhất, những gì tôi muốn làm là một cái gì đó như:
var myAction = (Action)(() => Console.WriteLine("yada yada"));
myAction.FireAndForget(); // async invocation
Thật không may, sự lựa chọn rõ ràng gọi BeginInvoke()
mà không có một EndInvoke()
tương ứng không hoạt động - nó kết quả trong một sự rò rỉ tài nguyên chậm (kể từ khi asyn nhà nước được tổ chức bởi thời gian chạy và không bao giờ phát hành ... nó mong đợi một cuộc gọi cuối cùng để EndInvoke()
.Tôi cũng không thể chạy mã trên hồ bơi NET thread. Bởi vì nó có thể mất một thời gian rất dài để hoàn thành (nó chỉ nên để chạy mã tương đối ngắn ngủi trên nhóm luồng) - điều này làm cho nó không thể sử dụng ThreadPool.QueueUserWorkItem()
.
Ban đầu, tôi chỉ cần hành vi này cho các phương thức có chữ ký khớp với Action
, Action<...>
hoặc Func<...>
. Vì vậy, tôi đặt cùng một tập hợp các phương pháp mở rộng (xem danh sách bên dưới) cho phép tôi thực hiện điều này mà không cần phải chạy vào tài nguyên bị rò rỉ. Có quá tải cho mỗi phiên bản Hành động/Func.
Thật không may, bây giờ tôi muốn chuyển mã này sang .NET 4, nơi số lượng các tham số chung về Hành động và Func đã được tăng lên đáng kể. Trước khi tôi viết một kịch bản T4 để tạo ra chúng, tôi cũng hy vọng tìm ra một cách đơn giản hơn để làm điều này. Bất kỳ ý tưởng được chào đón.
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<>
}
mát câu hỏi. Mã đau! Điều duy nhất họ có điểm chung là họ đều là MulticastDelegate. Tôi không chắc chắn sẽ có một cách ăn kiêng mã không an toàn. Làm thế nào để MS tạo ra tất cả nó ở nơi đầu tiên? – spender
@spender: Trình biên dịch sẽ tự động tạo ra các phương thức 'BeginInvoke' /' EndInvoke' trên mọi loại đại biểu được gõ mạnh mẽ vào các đối số của đại biểu. – LBushkin
Để chắc chắn. Dường như với tôi để khuyến khích mã boilerplate đáng kể, và tôi ngạc nhiên rằng .net4 đã không giới thiệu một cách chung chung hơn (hoặc siêu chung) tuyên bố các loại cấu trúc ... mặc dù tôi đồng ý rằng trong hầu hết các sane mã, danh sách tham số sẽ không vượt quá 5 hoặc lâu hơn. – spender