2010-12-13 37 views
23

Tôi có một ứng dụng tải các hội đồng bên ngoài mà tôi không có quyền kiểm soát (tương tự như một mô hình plugin nơi người khác tạo và phát triển các hội đồng được sử dụng bởi ứng dụng chính). Nó tải chúng bằng cách tạo ra các AppDomain mới cho các assembly này và sau đó khi các assembly được thực hiện đang được sử dụng, AppDomain chính sẽ loại bỏ chúng.Làm cách nào để tải xuống đúng AppDomain bằng C#?

Hiện nay, nó cách đơn giản unloads các hội đồng bởi

try 
{ 
    AppDomain.Unload(otherAssemblyDomain); 
} 
catch(Exception exception) 
{ 
    // log exception 
} 

Tuy nhiên, nhân dịp, trường hợp ngoại lệ được ném trong quá trình dỡ đặc biệt CannotUnloadAppDomainException. Từ những gì tôi hiểu, điều này có thể được dự kiến ​​từ một thread trong con AppDomains cannot be forcibly aborted due to situations where unmanaged code is still being executed or the thread is in a finally block:

Khi một thread gọi Unload, lĩnh vực mục tiêu được đánh dấu cho dỡ. các nỗ lực chỉ định chuyên dụng để xóa miền và tất cả các chuỗi trong miền bị hủy. Nếu một thread làm không hủy bỏ, ví dụ vì nó là thực thi mã không được quản lý, hoặc vì nó được thực hiện một khối finally, sau đó sau một khoảng thời gian một CannotUnloadAppDomainException là ném vào các chủ đề mà ban đầu gọi Unload. Nếu không thể hủy kết thúc chuỗi , tên miền đích không được tải. Vì vậy, trong các.NET Framework phiên bản 2.0 tên miền không được đảm bảo để dỡ bỏ, bởi vì nó có thể không được có thể chấm dứt thực hiện chủ đề.

Mối quan tâm của tôi là nếu lắp ráp không được tải thì có thể gây rò rỉ bộ nhớ. Một giải pháp tiềm năng sẽ là để giết chính quá trình ứng dụng chính nếu ngoại lệ trên xảy ra nhưng tôi thay vì tránh hành động quyết liệt này.

Tôi cũng đang cân nhắc việc lặp lại cuộc gọi tải cho một vài lần thử bổ sung. Có lẽ một vòng lặp hạn chế như thế này:

try 
{ 
    AppDomain.Unload(otherAssemblyDomain); 
} 
catch (CannotUnloadAppDomainException exception) 
{ 
    // log exception 
    var i = 0; 
    while (i < 3) // quit after three tries 
    { 
     Thread.Sleep(3000);  // wait a few secs before trying again... 
     try 
     { 
      AppDomain.Unload(otherAssemblyDomain); 
     } 
     catch (Exception) 
     { 
      // log exception 
      i++; 
      continue; 
     } 
     break; 
    } 
} 

Điều này có ý nghĩa không? Tôi có nên bận tâm với việc cố gắng dỡ hàng nữa không? Tôi có nên thử một lần và tiếp tục không? Tôi có nên làm gì khác không? Ngoài ra, có bất cứ điều gì có thể được thực hiện từ AppDomain chính để kiểm soát lắp ráp bên ngoài nếu chủ đề vẫn đang chạy (ghi nhớ những người khác đang viết và chạy mã bên ngoài này)?

Tôi đang cố gắng hiểu các phương pháp hay nhất khi quản lý nhiều AppDomain.

+4

Điều này là không thể trả lời. Nếu bạn không có quyền kiểm soát các chủ đề trong ứng dụng thì có rất ít lý do để hy vọng rằng một trong những chủ đề đó sẽ đột nhiên hợp tác 3 giây sau đó. Cô lập nó trong một quá trình là sửa chữa thực sự duy nhất. –

+0

Cảm ơn Hans. Có vẻ như đây là giới hạn của AppDomain so với các quy trình. Tôi đã hy vọng cho một cách để buộc phải giết một AppDomain trong bất kỳ hoàn cảnh giống như bạn có thể buộc phải giết một quá trình (như bạn đề nghị). –

+0

@HansPassant Thực hiện cách ly thực hiện cho một quy trình chuyên dụng có thực sự đảm bảo điều này có thể tránh được không? –

Trả lời

9

Tôi đã xử lý sự cố tương tự trong ứng dụng của mình. Về cơ bản, bạn không thể làm gì thêm để buộc AppDomain giảm xuống hơn Unload. Về cơ bản, nó hủy bỏ tất cả các chủ đề đang thực thi mã trong AppDomain và nếu mã đó bị kẹt trong trình hoàn thiện hoặc mã không được quản lý, thì không có nhiều việc có thể thực hiện được.

Nếu, dựa trên chương trình được đề cập, có khả năng mã finalizer/unmanaged sẽ hoàn thành một thời gian sau đó, bạn hoàn toàn có thể gọi lại Unload. Nếu không, bạn có thể làm rò rỉ tên miền trên mục đích hoặc chu trình quá trình.

0

Cố gắng tạo GC.Collect() nếu bạn không dỡ miền.

try 
    { 
     AppDomain.Unload(otherAssemblyDomain); 
    } 
    catch (CannotUnloadAppDomainException) 
    { 
     GC.Collect(); 
     AppDomain.Unload(otherAssemblyDomain); 
    } 
Các vấn đề liên quan