2013-04-12 32 views
11

Nếu tôi muốn làm một "lửa và quên" của một số mã, nhưng vẫn muốn đảm bảo rằng bộ nhớ của tôi được làm sạch (mỗi Why does asynchronous delegate method require calling EndInvoke?), sau đây sẽ đạt được mục tiêu đó?C# Là action.BeginInvoke (action.EndInvoke, null) là một ý tưởng hay?

Action myAction =() => LongRunTime(); 
myAction.BeginInvoke(myAction.EndInvoke,null); 

Tôi đã xem xét nhưng chưa thấy mẫu đó được sử dụng ở bất kỳ đâu. Thay vào đó, mọi người sử dụng phương pháp annonomoyus làm gọi lại của họ (chẳng hạn như The proper way to end a BeginInvoke?) hoặc họ xác định phương thức gọi lại thực tế. Vì tôi chưa từng thấy ai khác làm điều này, nó khiến tôi nghĩ rằng nó không hoạt động hoặc là một ý tưởng tồi.

Cảm ơn!

+1

Tôi không thấy bất kỳ sự cố nào khi sử dụng chuyển đổi nhóm phương thức qua biểu thức lambda. – Thorarin

+1

Nếu bạn có thể thêm câu trả lời theo cách này hay cách khác, nếu kỹ thuật này không gây rò rỉ bộ nhớ thì đó sẽ là câu trả lời mà tôi đang tìm kiếm. Cảm ơn! –

Trả lời

12

Sử dụng chuyển đổi nhóm phương pháp thay vì ủy quyền là tốt, EndInvoke sẽ vẫn được gọi trong số Action của bạn. Không có gì khác để được thực hiện, vì đây là một ngọn lửa và quên cuộc gọi. Thật không may, nó hơi khó để chứng minh trực tiếp rằng EndInvoke được gọi, vì Action là một đại biểu và chúng tôi không thể chỉ thêm một điểm ngắt trên một số lớp trong BCL.

Mã này sẽ (theo định kỳ) kiểm tra một số lĩnh vực riêng của IAsyncResult được trả về bởi BeginInvoke, mà dường như để theo dõi hay không EndInvoke đã được gọi nào:

public partial class MainWindow : Window 
{ 
    private Timer _timer = new Timer(TimerCallback, null, 100, 100); 
    private static IAsyncResult _asyncResult; 

    public MainWindow() 
    { 
     InitializeComponent(); 
    } 

    static void LongRunTime() 
    { 
     Thread.Sleep(1000); 
    } 

    void Window_Loaded(object sender, RoutedEventArgs args) 
    { 
     Action myAction =() => LongRunTime(); 
     _asyncResult = myAction.BeginInvoke(myAction.EndInvoke, null); 
    } 

    static void TimerCallback(object obj) 
    { 
     if (_asyncResult != null) 
     { 
      bool called = ((dynamic)_asyncResult).EndInvokeCalled; 
      if (called) 
      { 
       // Will hit this breakpoint after LongRuntime has completed 
       Debugger.Break(); 
       _asyncResult = null; 
      } 
     } 
    } 
} 

Tôi đã hai lần kiểm tra sử dụng SOS rằng không có bất kỳ rò rỉ bộ nhớ được quản lý nào. Tôi cũng đã thử một số bằng chứng khác, nhưng chúng hoàn toàn khác với chứng minh này, tôi nghĩ vậy.

Một số điều thú vị tôi đã khám phá trong quá trình điều tra: cuộc gọi myAction.BeginInvoke sẽ hiển thị trên các trình sử dụng thiết bị, nhưng myAction.EndInvoke thì không.

+0

Không có điểm nào mà tôi có thể thấy. Chỉ cần gọi LongRunTime() trực tiếp bên trong nhiệm vụ. –

+3

Tác vụ ngoài điểm, nhưng đây là một ví dụ không có Tác vụ nếu bạn thích. – Thorarin

+0

+1 Cảm ơn Thorarin! Chi tiết và nghiên cứu tuyệt vời! –

Các vấn đề liên quan