2013-02-19 40 views
19

Xem phân tích hiệu suất đồng thời sau đại diện cho các công việc được thực hiện bởi một foreach song song:Tại sao tôi có khóa ở đây?

enter image description here

Bên trong vòng lặp mỗi thread đọc dữ liệu từ DB và xử lý nó. Không có khóa giữa các luồng vì mỗi luồng xử lý dữ liệu khác nhau.

Có vẻ như có các ổ khóa định kỳ trong tất cả các chủ đề của foreach vì lý do không xác định (xem hình chữ nhật dọc màu đen). Nếu bạn thấy phân đoạn bị khóa đã chọn (phần màu đỏ đậm), bạn sẽ thấy rằng ngăn xếp hiển thị chuỗi bị khóa tại hàm dựng StockModel.Quotation. Mã ở đó chỉ xây dựng hai danh sách trống!

Tôi đã đọc ở đâu đó rằng điều này có thể được gây ra bởi GC vì vậy tôi đã thay đổi thu gom rác thải để chạy trong chế độ máy chủ với:

<runtime> 
    <gcServer enabled="true"/> 
</runtime> 

tôi nhận được một cải tiến nhỏ (khoảng 10% - 15 % nhanh hơn) nhưng tôi vẫn có khóa dọc ở khắp mọi nơi.

Tôi cũng đã thêm vào tất cả các truy vấn DB WITH (NOLOCK) vì tôi chỉ đọc dữ liệu mà không có bất kỳ sự khác biệt nào.

Bất kỳ gợi ý nào về những gì đang xảy ra ở đây?

Máy tính nơi phân tích đã được thực hiện có 8 lõi.

EDIT: Sau khi bật máy chủ Microsoft Symbol, mọi chủ đề bị chặn trong các cuộc gọi như wait_gor_gc_done hoặc WaitUntilGCComplete. Tôi nghĩ rằng cho phép GCServer tôi đã có một GC cho mỗi thread vì vậy tôi sẽ tránh khóa "dọc" nhưng có vẻ như nó không phải là trường hợp. Liệu tôi có sai?

Câu hỏi thứ hai: vì máy không bị áp lực bộ nhớ (5 trong số 8 hợp đồng biểu diễn) có cách trì hoãn việc thực thi GC hoặc tạm dừng cho đến khi kết thúc song song (hoặc cấu hình để cháy ít thường xuyên hơn))?

+0

Trong trường hợp bạn đang phân bổ rất nhiều đối tượng và khóa thực sự do GC gây ra, bạn có cố gắng buộc GC.Collect ngay trước khi bắt đầu công việc TPL không? GC.Collect với GCCollectionMode.Forced. – Alex

+0

Vâng, bên trong vòng lặp tôi đang phân bổ một số lượng lớn các đối tượng nhỏ bị 'bỏ rơi' ở cuối mỗi lần lặp lại. Điều này có thể khóa toàn bộ các chủ đề nếu chúng được GC'ed? –

+4

Bật Microsoft Symbol Server để nhận các dấu vết ngăn xếp tốt hơn. Do chờ đợi lâu, điều này trông giống như các bộ sưu tập rác đơn giản. –

Trả lời

0

Bạn có thể thử sử dụng GCLatencyMode.LowLatency; Xem câu hỏi liên quan ở đây: Prevent .NET Garbage collection for short period of time

Gần đây tôi đã cố gắng này không có may mắn. Bộ sưu tập rác vẫn đang được gọi khi lưu trữ hình ảnh bitmap của các kích thước Biểu tượng trên biểu mẫu tôi đang hiển thị. Những gì làm việc cho tôi đã được sử dụng kiến ​​thức hiệu suất Ants và Reflector để tìm các cuộc gọi chính xác đã gây ra GC.Collect và làm việc xung quanh nó.

+0

Không có gì lạ ở đây. Tôi đang phân bổ số lượng lớn các đối tượng trong một khoảng thời gian ngắn nhưng máy có rất nhiều RAM vì vậy tôi muốn "dừng" trong một thời gian GC nếu tôi có thể làm điều đó. –

+0

Bạn có phân bổ xảy ra bên trong một vòng lặp? Chúng có thể được di chuyển ra ngoài không? xem http://stackoverflow.com/questions/3412003/allocating-memory-inside-loop-vs-outside-loop – Kim

+0

Mmmm, mỗi vòng lặp đọc dữ liệu riêng của nó từ một db, xử lý nó và tạo ra một kết quả mà nó được lưu trữ để tiếp tục Chế biến. Sau đó dữ liệu được tạo sẽ bị hủy. Tôi không thấy cách nào để tránh điều đó ... –

3

Nếu lớp StockModel.Quotation của bạn cho phép, bạn có thể tạo một nhóm để giới hạn số đối tượng mới được tạo. Đây là một kỹ thuật mà đôi khi họ sử dụng trong các trò chơi để ngăn chặn các nhà sưu tập rác ngừng hoạt động ở giữa màn hình.

Dưới đây là một việc thực hiện hồ bơi cơ bản:

class StockQuotationPool 
    { 

     private List<StockQuotation> poolItems; 
     private volatile int itemsInPool; 

     public StockQuotationPool(int poolSize) 
     { 
      this.poolItems = new List<StockQuotation>(poolSize); 
      this.itemsInPool = poolSize; 

     } 

     public StockQuotation Create(string name, decimal value) 
     { 
      if (this.itemsInPool == 0) 
      { 
       // Block until new item ready - maybe use semaphore. 
       throw new NotImplementedException(); 
      } 

      // Items are in the pool, but no items have been created. 
      if (this.poolItems.Count == 0) 
      { 
       this.itemsInPool--; 
       return new StockQuotation(name, value); 
      } 

      // else, return one in the pool 
      this.itemsInPool--; 

      var item = this.poolItems[0]; 
      this.poolItems.Remove(item); 

      item.Name = name; 
      item.Value = value; 

      return item; 
     } 

     public void Release(StockQuotation quote) 
     { 
      if (!this.poolItems.Contains(quote) 
      { 
       this.poolItems.Add(quote); 
       this.itemsInPool++; 
      } 
     } 

    } 

Đó là giả định rằng StockQuotation trông giống như sau:

class StockQuotation 
    { 
     internal StockQuotation(string name, decimal value) 
     { 
      this.Name = name; 
      this.Value = value; 
     } 


     public string Name { get; set; } 
     public decimal Value { get; set; } 
    } 

Sau đó, thay vì gọi các nhà xây dựng StockQuotation mới(), bạn hỏi hồ bơi cho một ví dụ mới. Hồ bơi trả về một cá thể hiện có (bạn có thể xử lý trước chúng nếu bạn muốn) và đặt tất cả các thuộc tính sao cho nó trông giống như một cá thể mới.Bạn có thể cần phải chơi xung quanh cho đến khi bạn tìm thấy một kích thước hồ bơi đủ lớn để chứa các chủ đề cùng một lúc.

Đây là cách bạn muốn gọi từ chuỗi.

// Get the pool, maybe from a singleton. 
    var pool = new StockQuotationPool(100); 


    var quote = pool.Create("test", 1.00m); 


    try 
    { 
     // Work with quote 

    } 
    finally 
    { 
     pool.Release(quote); 
    } 

Cuối cùng, lớp này không an toàn tại thời điểm này. Hãy cho tôi biết nếu bạn cần bất kỳ trợ giúp nào để thực hiện điều đó.

+0

Ý tưởng hay, tôi sẽ nghĩ về điều này. Vấn đề là vì điều này trước đây không được coi là tôi không chắc chắn về logic cần thiết để biết khi nào việc phát hành có thể được gọi. –

+0

Tôi đã cập nhật mã để bao gồm lần thử/cuối cùng. Bạn không muốn mất các đối tượng khi ngoại lệ xảy ra. –

+0

Trong điều khoản của khi gọi pool.Release(), tôi muốn nói về cơ bản chỉ trước khi nó đi ra khỏi phạm vi nếu nó trong một phương pháp. Nếu nó được lưu trữ trong một trường trên một đối tượng, thì bạn có thể tạo đối tượng đó một lần và gọi Release() trong phương thức Dispose. ** Bạn càng sớm có thể phát hành nó, thì càng tốt ** và hồ bơi của bạn càng nhỏ. –

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