2016-08-02 13 views
16

Tôi đang nghiên cứu khả năng viết lại một dịch vụ tương đối nhỏ từ C++ đến C#. Dịch vụ có hai chức năng chính:Tôi có thể yêu cầu .NET GC để lại một số chủ đề không?

  1. Thực thi các yêu cầu HTTP một lần trong một thời gian. Chúng liên quan đến một số nhiệm vụ cấp cao như mã hóa/giải mã JSON, mã hóa/giải mã base64 và các yêu cầu HTTP, mà C++ không phải là tuyệt vời;
  2. Thực hiện một số tác vụ liên quan đến âm thanh trong thời gian thực có thời hạn khó, C# không tuyệt vời.

Các tác vụ thời gian thực được xử lý bởi một thư viện riêng có nội dung luồng riêng và hầu như không tương tác với phần còn lại của dịch vụ. Phần còn lại của dịch vụ cung cấp cho nó một chút dữ liệu, thu được từ các yêu cầu HTTP, cứ 5 phút một lần hoặc lâu hơn.

Vấn đề là, vì phần thời gian thực có thời hạn khó, tôi không thể chịu đựng được tạm dừng GC trên các chủ đề của thư viện. Về phía mã của riêng tôi, cần có rất nhiều thời gian để GC chạy giữa các yêu cầu Web, nhưng tôi không thể chịu đựng được rằng nó khởi động trong khi tôi đang cố gắng nạp dữ liệu vào thư viện.

I found mà tôi có thể tạo một phần quan trọng trong đó bộ thu gom rác sẽ không bắt đầu sử dụng GC.TryStartNoGCRegion(), giải quyết một nửa vấn đề.

Tuy nhiên, tôi vẫn không biết liệu có cách nào để báo cho .NET GC không để lại các chuỗi cụ thể không chạy mã được quản lý. Điều đó có thể không?

+0

Xem [bài đăng này] (http://stackoverflow.com/questions/6005865/prevent-net-garbage-collection-for-short-period-of-time). Nó đi kèm với tất cả các loại cảnh báo. –

+0

@ScottHannen, tôi tình cờ gặp nó (tôi liên kết với nó trong đoạn về GC.TryStartNoGCRegion). Tôi không nghĩ rằng nó cho thấy bất cứ điều gì về loại trừ các chủ đề từ bộ sưu tập rác thải. – zneak

+6

CLR không thể tạm dừng một chuỗi đang bận chạy mã gốc. Cũng không phải, không có gốc rễ đối tượng nào có thể thay đổi trong khi nó đang chạy mã như vậy. Vì vậy, không cần phải "hỏi", chỉ cần đảm bảo mã thời gian quan trọng của bạn là mã gốc và nó không trở lại phương thức được quản lý trong khi thực hiện công cụ của nó và không có gì có thể xảy ra sai. –

Trả lời

1

Như bạn đã chỉ ra, khi một ứng dụng được cấu hình để chạy trong chế độ Workstation GC, bộ thu gom rác sẽ không treo các luồng thực thi mã gốc. Trong trường hợp đó bất cứ lúc nào dịch vụ của bạn nhận được một yêu cầu tôi sẽ cố gắng một cái gì đó như thế này ...

private bool _running = true; 
    private int _workCounter = 0; 
    private AutoResetEvent _workFlag = new AutoResetEvent(false); 
    private void RunNoGCNativeCode(params object[] args) 
    { 
     // Increase the work counter to determine how many requests are being processed 
     if (Interlocked.Increment(ref _workCounter) == 1) 
     { 
      // Try to start a No GC Region 
      GC.TryStartNoGCRegion(1 * 1024 * 1024 * 1024, true); 
     } 
     // TODO: Prep data and execute your native code 
     // TODO: Process response 
     // TODO: Dispose of anything that is no longer in use and null objects as needed 
     if (Interlocked.Decrement(ref _workCounter) == 0 && GCSettings.LatencyMode == GCLatencyMode.NoGCRegion) 
     { 
      GC.EndNoGCRegion(); 
     } 
     // Notify Manual Collection thread work has been completed 
     _workFlag.Set(); 
    } 

Trên một chủ đề khác nhau ...

private void WaitForNoWorkThenGC() 
    { 
     // Continue running thread while in use 
     while (_running) 
     { 
      // Wait for some work to be complete 
      _workFlag.WaitOne(); 
      // If there is no work being processed call GC.Collect() 
      if (_workCounter == 0) 
      { 
       GC.Collect(); 
      } 
     } 
    } 

Điều này sẽ giúp bạn kiểm soát khi GC xảy ra để giảm thiểu tác động trên ứng dụng của bạn

Đối với mã được quản lý, tôi không tìm thấy bất kỳ dấu hiệu nào cho thấy rằng các chuỗi cụ thể của .NET Garbage Collection có thể "để lại một mình" thực thi mã được quản lý. Tất cả các chế độ hiện tại (Server, Workstation, Concurrent, vv) của bộ sưu tập rác sẽ đình chỉ TẤT CẢ các chủ đề thực thi mã được quản lý khi nó chọn. Lưu ý rằng một số chế độ GC có thể tạm dừng ngắn hơn trong các chủ đề cũng có thể hữu ích. Tôi đã thử sử dụng TryStartNoGCRegion() trong một ứng dụng C# thời gian thực và nó bỏ qua yêu cầu của tôi luôn vì lượng bộ nhớ tôi sử dụng, tôi không thể tìm cách chỉ định thu thập sau khi đạt tới giới hạn bộ nhớ X.

Tuy nhiên, bạn có thể xem xét hai giải pháp tiềm năng.

  1. Sử dụng Garbage Collection Notifications để theo dõi khi GC đang tiến hành thu gom rác đầy đủ. Có khả năng nếu bạn giải phóng bộ nhớ một cách thủ công trước khi nó đạt đến điểm thu gom, bạn có thể tránh Garbage Collection.
  2. Chạy loại yêu cầu khác nhau trong các quy trình ứng dụng riêng biệt? hoặc vượt qua chúng để tách các quy trình ứng dụng khi bạn nhận được chúng. Bằng cách này, mỗi ứng dụng không chia sẻ bộ sưu tập của thùng rác và bối cảnh sẽ được xử lý riêng.
+1

Tôi đánh giá cao nỗ lực nghiên cứu, nhưng tôi muốn chỉ ra rằng có * là * tài liệu chứng minh rằng các máy trạm thu rác sẽ không làm gián đoạn một chủ đề đang chạy mã nguồn gốc. Xem [Nguyên tắc cơ bản của bộ sưu tập rác] (https://msdn.microsoft.com/en-us/library/ee787088 (v = vs.110) .aspx # workstation_and_server_garbage_collection), cuộn xuống "So sánh máy trạm và máy chủ thu gom rác". – zneak

+0

Ah có câu trả lời của tôi là cụ thể cho các chủ đề chạy mã được quản lý. Đối với trước và sau khi ứng dụng .NET của bạn đang gọi mã nguồn gốc của bạn. Nói cách khác, bạn trả lời câu hỏi của riêng bạn, "Làm thế nào để nói .NET không đình chỉ các chủ đề thực thi mã nguồn gốc trong GC? - Cấu hình ứng dụng của bạn để sử dụng chế độ GC máy trạm, Xem: Elementhttps: //msdn.microsoft.com/en- chúng tôi/thư viện/ms229357% 28v = vs.110% 29.aspx? f = 255 & MSPPError = -2147217396 " – xer21

+0

@zneak Tôi đã cập nhật phản hồi của mình để phản ánh. – xer21

-2

Không kiểm tra xem nó nhưng, tôi sẽ kế thừa Chủ đề, hoặc Task bất cứ điều gì bạn sử dụng như:

public class SuppressedThread : Thread 
{    
    protected override void Finalize() 
    { 
     try 
     { 
      // kill what you want 
     } 
     finally 
     { 
      base.SupressFinalizeFinalize(); 
     } 
    } 
} 

public class SuppressedThread : Thread, IDisposable 
{  
    protected override void Dispose() 
    { 
     try 
     { 
      // kill what you want 
     } 
     finally 
     { 
      GC.SupressFinalize(); 
     } 
    } 
} 

sau đó bạn có thể sử dụng nó thường

var newThread = new SuppressedThread(DoWork); 
newThread.Start(42); 

gì bạn nghĩ?

+3

Điều này có nhiều vấn đề. 1) 'Thread' được niêm phong; bạn không thể kế thừa từ nó. 2) Ngay cả khi bạn có thể kế thừa từ nó, điều này sẽ chỉ ngăn GC của chính nó, chứ không phải các đối tượng được tạo trên luồng. 3) Phương thức 'Finalize' không thể được ghi đè trong C#. Thay vào đó, cú pháp finalizer được sử dụng, như '~ Foo()'. – vcsjones

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