2009-06-11 10 views
11

tôi sử dụng phương pháp sau đây trong một đoạn mã sản xuất:.NET: Tôi có cần giữ tham chiếu đến WebClient khi tải xuống không đồng bộ không?

private void DownloadData(Uri uri) 
{ 
    WebClient webClient = new WebClient(); 
    DownloadDataCompletedEventHandler eh = null; 
    eh = delegate(object sender, DownloadDataCompletedEventArgs e) 
     { 
      webClient.DownloadDataCompleted -= eh; 
      ((IDisposable) webClient).Dispose(); 
      OnDataDownloaded(); 
     }; 
    webClient.DownloadDataCompleted += eh; 
    webClient.DownloadDataAsync(uri); 
} 

Tôi bây giờ lo lắng rằng một khó khăn để tái tạo lỗi có thể được gây ra bởi WebClient dụ là thu gom rác thải trước khi sự kiện DownloadDataCompleted được gọi là: sau khi thoát khỏi phương pháp DownloadData() của tôi, không có tham chiếu rõ ràng đối tượng WebClient, do đó có thể xảy ra một cách đáng tin cậy.

Vì vậy, câu hỏi của tôi là: điều này có thể xảy ra thực tế không? Tôi không thể tái tạo vấn đề, vì vậy có thể có một số vấn đề nội tại xảy ra ngăn không cho đối tượng WebClient bị thu gom rác thải (ví dụ: đối tượng có thể tự đăng ký với đối tượng chung ở đâu đó trong khi chờ phản hồi).

Mã đang chạy trên .NET 2.0 nếu điều đó tạo ra bất kỳ sự khác biệt nào.

Trả lời

5

Tôi không biết chắc chắn liệu WebClient có thể bình thường được thu thập rác hay không trong khi đang hoạt động không đồng bộ, vì có thể có tham chiếu nội bộ - nhưng câu hỏi lớn hơn là: có quan trọng không?

Vì vậy, miễn là đủ số WebClient vẫn "sống" để phục vụ yêu cầu và gọi trình xử lý của bạn, cho dù chính đối tượng WebClient có bị thu gom rác không?

Tài liệu WebClient không đề cập đến bất kỳ điều gì về việc phải giữ một tham chiếu (ví dụ: System.Threading.Timer docs), vì vậy tôi nghĩ rằng điều này là hợp lý.

Trong trường hợp cụ thể này, đại biểu của bạn có tham chiếu đến WebClient, miễn là bản thân đại biểu được tham chiếu, không thể là WebClient. Dự đoán của tôi là một số phần của hệ thống ở đâu đó cần giữ lại cuộc gọi để biết phải làm gì khi lưu lượng truy cập mạng đến và cuộc gọi đó cuối cùng sẽ dẫn đến đại biểu của bạn, vì vậy bạn không sao.

+1

Bạn đương nhiên đúng là câu hỏi quan trọng là liệu người được ủy thác có thu gom rác hay không. Và có, ta nên nghĩ rằng hệ thống hoàn thành IO cần giữ một tham chiếu (gián tiếp) cho đại biểu. Nhưng có vẻ như hoàn toàn không thể là tham chiếu này có thể là một WeakReference. –

+1

Yup, không phải là không thể. Nhưng tôi sẽ * hy vọng * đó sẽ được ghi lại (vì nó là cho Timer). –

0

Flipside của đồng xu ... nếu bạn đã lưu trữ tham chiếu đến WebClient ở đâu đó, chỉ để xem liệu nó có tạo ra sự khác biệt ... điều đó có làm cho vấn đề biến mất hoàn toàn không? Nó có thể được dễ dàng hơn để kiểm tra nó theo cách này và chắc chắn hơn để đoán tại những gì có vẻ hợp lý.

1

Bạn có thể thử gỡ lỗi ứng dụng bằng Công cụ gỡ lỗi cho Windows - nó cho phép bạn xem chính xác những gì đang giữ tham chiếu đến một đối tượng cụ thể (with the appropriate plug-in). Rất hữu ích cho các trường hợp như vậy.

Tôi không biết câu trả lời cho câu hỏi của bạn. Một khả năng là trong suốt thời gian hoạt động, WebClient tự tạo ra một trong những đối tượng "root", không bao giờ được thu thập rác (một ứng dụng .NET thường có khoảng 5 đến 10 đối tượng như vậy, là gốc của một số cây tham chiếu được sử dụng. bởi ứng dụng). Đây là suy đoán thuần túy, mặc dù.

0

Khi tạo một phương thức ẩn danh với tham chiếu đến biến phạm vi (webClient trong trường hợp của bạn) và tạo biến riêng với tham chiếu đến đối tượng đó. Vì vậy, như jon là đoán đại biểu của bạn sẽ giữ một tham chiếu đến webClient và trước khi các đại biểu unregisters nó tự webClient không thể được thu gom rác thải.

Tuy nhiên, tôi thường khuyên bạn không nên sử dụng tham chiếu webClient trong phương thức ủy nhiệm của bạn nhưng để truyền người gửi đến biến nội bộ. sử dụng các biến bên ngoài để các phương thức nặc danh có thể dẫn đến một số lỗi rất lạ.

10

Không, đối tượng của bạn sẽ không phải là GC-ed cho đến khi hoàn thành gọi lại. Theo số Does the Garbage Collector destroy temporarily unreferenced objects during async calls in .NET?, " API không đồng bộ giữ tham chiếu đến yêu cầu của bạn (trong nhóm chủ đề mà các hoạt động IO không đồng bộ được nộp) và vì vậy nó sẽ không được thu gom rác cho đến khi hoàn thành."

Nhưng, mã của bạn cũng đang làm công cụ mà nó không cần phải: bạn không cần phải tách trình xử lý sự kiện và không cần phải gọi Dispose trên webclient. (Dispose() thực sự không được thực hiện bởi WebClient-- bạn có thể thấy điều này trong nguồn tham chiếu .NET Framework tại http://referencesource.microsoft.com/netframework.aspx).

Vì vậy, bạn thực sự không cần phải tham khảo cá thể webclient trong cuộc gọi lại của mình. Nói cách khác, mã sau đây cũng sẽ hoạt động tốt và tránh bất kỳ vấn đề tiềm ẩn nào (được thảo luận ở trên) về việc tham chiếu các biến cục bộ bên ngoài từ bên trong một đại biểu.

private void DownloadData(Uri uri) 
{ 
    WebClient webClient = new WebClient(); 
    DownloadDataCompletedEventHandler eh = null; 
    eh = delegate(object sender, DownloadDataCompletedEventArgs e) 
    { 
     OnDataDownloaded(); 
    }; 
    webClient.DownloadDataCompleted += eh; 
    webClient.DownloadDataAsync(uri); 
} 

Dù sao, bạn có thể muốn tìm nơi khác để tìm lỗi. Một nơi tôi muốn xem là kết quả của cuộc gọi HTTP-- bạn có thể đang hết bộ nhớ, có thể đang chạy vào lỗi máy chủ, v.v. Bạn có thể xem e.Error để xem các cuộc gọi có thực sự hoạt động hay không.

+0

Bạn quên đính kèm trình xử lý sự kiện: 'webClient.DownloadDataCompleted + = eh;' –

+0

cố định. Cảm ơn vì đã cho tôi biết. vui mà không ai khác nhìn thấy điều này trong 7 năm kể từ khi tôi viết câu trả lời này. ;-) –

+0

Heh, yeah. Tôi đã có một câu hỏi ngày hôm qua @ http://stackoverflow.com/questions/39609125/object-disposal-and-garbage-collection-prior-to-event-triggering/39609469#39609469 và câu trả lời của bạn đã được tìm thấy là một tài nguyên có giá trị. –