2009-06-10 41 views
12

Tôi có đối tượng này PreloadClient mà thực hiện IDisposable, tôi muốn vứt bỏ nó, nhưng sau khi các phương pháp không đồng bộ kết thúc cuộc gọi của họ ... mà không xảy raLàm thế nào để vứt bỏ các đối tượng có các phương thức không đồng bộ được gọi là?

private void Preload(SlideHandler slide) 
    { 
     using(PreloadClient client = new PreloadClient()) 
     {     
      client.PreloadCompleted += client_PreloadCompleted; 
      client.Preload(slide); 
     } 
     // Here client is disposed immediately 
    } 
    private void client_PreloadCompleted(object sender, SlidePreloadCompletedEventArgs e) 
    { 
    // this is method is called after a while, 
    // but errors are thrown when trying to access object state (fields, properties) 
    } 

Vì vậy, bất kỳ ý tưởng hoặc quanh công việc ??

Trả lời

11
  1. Bạn không nên sử dụng using xây dựng, nhưng thay vì vứt bỏ đối tượng của bạn khi họ không còn cần thiết:

    // keep a list of strong references to avoid garbage collection, 
    // and dispose them all in case we're disposing the encapsulating object 
    private readonly List<PreloadClient> _activeClients = new List<PreloadClient>(); 
    private void Preload(SlideHandler slide) 
    { 
        PreloadClient client = new PreloadClient(); 
        _activeClients.Add(client); 
        client.PreloadCompleted += client_PreloadCompleted; 
        client.Preload(slide); 
    } 
    
    private void client_PreloadCompleted(object sender, 
        SlidePreloadCompletedEventArgs e) 
    { 
        PreloadClient client = sender as PreloadClient; 
    
        // do stuff 
    
        client.PreloadCompleted -= client_PreloadCompleted; 
        client.Dispose(); 
        _activeClients.Remove(client); 
    } 
    
  2. trong trường hợp này, bạn phải vứt bỏ tất cả khách hàng khi xử lý chính lớp:

    protected override Dispose(bool disposing) 
    { 
        foreach (PreloadClient client in _activeClients) 
        { 
         client.PreloadCompleted -= client_PreloadCompleted; 
         client.Dispose(); 
        } 
        _activeClients.Clear(); 
        base.Dispose(disposing); 
    } 
    
  3. Lưu ý rằng việc thực hiện này không phải là thread an toàn

    • Truy cập vào danh sách _activeClientsphải được made thread-safe, như phương pháp PreloadCompleted của bạn được gọi từ một thread khác nhau
    • đối tượng chứa của bạn có thể được xử lý trước khi một khách hàng bắn sự kiện này. Trong trường hợp đó "làm công cụ" không nên làm gì cả, vì vậy đây là một điều khác bạn nên chăm sóc.
    • Nó có thể là một ý tưởng tốt để sử dụng một khối try/finally bên trong xử lý sự kiện của bạn, để đảm bảo rằng các đối tượng được xử lý trong mọi trường hợp
+1

Tôi tin rằng đây là một câu trả lời tuyệt vời –

+0

Một mẫu có thể áp dụng ở đây và tôi muốn sử dụng thường xuyên hơn, sẽ có khách hàng bao gồm thuộc tính 'IsDisposed', có' _activeClients' giữ một danh sách 'WeakReference' cho khách hàng, và có hành động thêm một mục vào' _activeClients' kiểm tra một vài mục để xem liệu 'WeakReference' đã chết hay mục tiêu được xử lý," hoán đổi "nó với mục tiếp theo nếu vậy. Điều đó sẽ tránh bất kỳ cần phải khóa danh sách trong gọi lại không đồng bộ, và cũng thay thế O (N) chi phí của việc loại bỏ từng mục từ danh sách với một O (1) thêm chi phí khi mục được thêm vào. – supercat

+0

Bạn có thể giải thích tại sao việc vứt bỏ tốt hơn là sử dụng ở đây - thx hay không. – niico

1

tôi có một vài ý tưởng:

  1. thay đổi kiến ​​trúc của bạn.
  2. định đoạt trong xử lý
  3. sử dụng EventWaitHandle
+0

việc kiểm tra này ra về EventWaitHandle: http: // wekempf.spaces.live.com/Blog/cns!D18C3EC06EA971CF!672.entry?sa=571623833 –

+0

tất cả các điểm tốt –

3

Tại sao không vứt bỏ các khách hàng trong gọi lại?

+1

Nếu bạn có từ 2 sự kiện trở lên ... mà bạn chọn để hủy bỏ :)? –

+0

@jalchr: Nếu bạn đã triển khai "Vứt bỏ" * một cách chính xác *, việc hủy bỏ nhiều lần một lần không ảnh hưởng đến kết quả cho dù có bao nhiêu thời gian "Vứt bỏ" được gọi. – Sung

+1

@jalchr: Nếu bạn cần vứt bỏ một đối tượng khi gọi lại lần cuối, hãy sử dụng một số trường khóa hoặc khóa liên động được bảo vệ để theo dõi số lượng cuộc gọi đang tồn tại và có từng cuộc gọi lại kiểm tra xem đó có phải là lần cuối cùng hay không. . – supercat

0

Nếu có trình xử lý sự kiện được đăng ký, bạn không thể bỏ đối tượng trong khi có các sự kiện có thể được gọi trên đó. Đặt cược tốt nhất của bạn là làm cho lớp có chứa một lần & lưu trữ máy khách trong một biến lớp, được xử lý khi lớp chứa.

Something như

class ContainingClass : IDisposable 
{ 
    private PreloadClient m_Client; 

    private void Preload(SlideHandler slide) 
    { 
     m_Client = new PreloadClient()) 

     m_Client.PreloadCompleted += client_PreloadCompleted; 
     m_Client.Preload(slide); 

    } 
    private void client_PreloadCompleted(object sender, SlidePreloadCompletedEventArgs e) 
    { 
    } 

    public void Dispose() 
    { 
     if (m_Client != null) 
      m_Client.Dispose(); 
    } 
} 
+0

Ý tưởng tồi. Bây giờ bạn gắn đời của lớp chứa với tuổi thọ của máy khách. Nếu Preload được gọi nhiều hơn một lần thì sao? Bạn sẽ không vứt bỏ khách hàng. Điều gì sẽ xảy ra nếu bạn gọi xử lý trên vùng chứa trước khi hoạt động không đồng bộ hoàn tất? Rất có thể bạn sẽ có ngoại lệ xấu xí –

0

Vâng, xử lý một đối tượng được sử dụng để tiêu diệt nguồn lực mà bạn không muốn giữ cho đến khi GC (cuối cùng) đến và thu thập đối tượng của bạn. Phương pháp vứt bỏ của bạn có giết bất cứ thứ gì bạn cần trong số client_PreloadCompleted không?

Bạn có thể có đối tượng vứt bỏ chính nó, khi tất cả các cuộc gọi lại đã xảy ra: Giữ "bộ đếm tham chiếu" cho mỗi cuộc gọi lại mà bạn mong đợi và giảm trên mỗi lần gọi lại - kiểm tra null ở cuối trình xử lý gọi lại và hủy bỏ nếu vậy.

Cách giải quyết khác: Đừng lo lắng về số IDisposable. GC sẽ thu thập đối tượng của bạn. Bạn có thể không muốn một trình xử lý gọi lại (có thể không được kích hoạt) để có trạng thái quan trọng. Nó (gọi lại) nên chỉ cần mở bất kỳ tài nguyên cần thiết khi nó được gọi và đóng chúng sau đó.

0

Chờ đợi không đồng bộ và xử lý xác định không pha trộn rất tốt. Nếu bạn có thể tìm thấy cách tách mã như vậy mà các công cụ dùng một lần đi trong một lớp và các sự kiện diễn ra trong một lớp khác, điều đó sẽ làm mọi thứ đơn giản hơn.

+0

Bạn có thể giải thích thêm ... ý của bạn là gì khi đặt các sự kiện vào một lớp khác ...? –

+0

Thực hiện một lớp giả định giữ trạng thái yêu cầu dọn dẹp xác định. Lớp đó là một ứng cử viên tốt cho IDisposable. Tuy nhiên, nếu bạn gọi một phương thức không đồng bộ trên nó, thì bạn phải đợi phương thức không đồng bộ hoàn tất trước khi bạn có thể xử lý nó. Quan tâm làm gì? Bạn cũng có thể làm cho nó một phương thức đồng bộ và vứt bỏ đối tượng khi phương thức hoàn thành. Bạn đã hoàn thành cùng một điều trong cùng một khoảng thời gian. –

0

Tại sao không xử lý theo phương pháp client_PreloadCompleted? Tương tự như những gì thecoop cung cấp, chỉ với các cuộc gọi Dispose bên trong phương pháp trên, sau khi bạn đã truy cập tất cả các dữ liệu cần thiết từ bên trong đối tượng khách hàng.

Chỉnh sửa: Tôi nghĩ đó cũng là những gì mà orialmog cung cấp.

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