2012-03-15 31 views
52

Có hiệu quả đểSpinWait vs Sleep đang chờ. Mà một trong những sử dụng?

SpinWait.SpinUntil(() => myPredicate(), 10000) 

cho một thời gian chờ của 10000ms

hoặc

Có hiệu quả hơn để sử dụng Thread.Sleep bỏ phiếu cho cùng một điều kiện Ví dụ một cái gì đó dọc theo dòng trong các cách sau SleepWait chức năng:

public bool SleepWait(int timeOut) 
{ 
    Stopwatch stopwatch = new Stopwatch(); 
    stopwatch.Start(); 
    while (!myPredicate() && stopwatch.ElapsedMilliseconds < timeOut) 
    { 
     Thread.Sleep(50) 
    } 
    return myPredicate() 
} 

Tôi là con cerned rằng tất cả các năng suất của SpinWait có thể không phải là một mô hình sử dụng tốt nếu chúng ta đang nói về thời gian chờ trên 1 giây? Đây có phải là giả định hợp lệ không?

Bạn thích phương pháp nào và tại sao? Có cách tiếp cận nào tốt hơn nữa không?


Cập nhật - Trở thành cụ thể hơn:

Có cách nào để làm cho BlockingCollection xung a thread ngủ khi nó đạt đến khả năng bị chặn? Tôi thích tránh một sự bận rộn chờ đợi như thường lệ như Marc Gravel gợi ý.

Trả lời

39

Cách tiếp cận tốt nhất là phải có một số cơ chế để tích cực phát hiện điều trở thành sự thật (chứ không phải là một cách thụ động bỏ phiếu cho nó có trở thành true); đây có thể là bất kỳ kiểu chờ xử lý nào hoặc có thể là Task với Wait hoặc có thể là event mà bạn có thể đăng ký để bỏ chọn chính mình. Tất nhiên, nếu bạn thực hiện kiểu "chờ đợi cho đến khi điều gì đó xảy ra", đó là vẫn là không hiệu quả như chỉ đơn giản là có một bit công việc tiếp theo được thực hiện làm gọi lại, có nghĩa là: bạn không cần sử dụng chuỗi đợi. TaskContinueWith cho việc này, hoặc bạn chỉ có thể thực hiện công việc trong một event khi nó bị sa thải. event có lẽ là cách tiếp cận đơn giản nhất, tùy thuộc vào ngữ cảnh. Tuy nhiên, Task, đã cung cấp hầu hết mọi thứ bạn đang nói đến ở đây, bao gồm cả cơ chế "chờ với thời gian chờ" và "gọi lại".

Và vâng, quay trong 10 giây không phải là điều tuyệt vời. Nếu bạn muốn sử dụng một cái gì đó như mã hiện tại của bạn và nếu bạn có lý do để mong đợi một sự chậm trễ ngắn, nhưng cần phải chờ lâu hơn - có thể SpinWait cho (nói) 20ms và sử dụng Sleep cho phần còn lại?


Viết lại nhận xét; dưới đây là cách tôi muốn treo một "là nó đầy đủ" cơ chế:

private readonly object syncLock = new object(); 
public bool WaitUntilFull(int timeout) { 
    if(CollectionIsFull) return true; // I'm assuming we can call this safely 
    lock(syncLock) { 
     if(CollectionIsFull) return true; 
     return Monitor.Wait(syncLock, timeout); 
    } 
} 

với, trong "đưa trở lại vào bộ sưu tập" mã:

if(CollectionIsFull) { 
    lock(syncLock) { 
     if(CollectionIsFull) { // double-check with the lock 
      Monitor.PulseAll(syncLock); 
     } 
    } 
} 
+0

Cảm ơn, tôi thích câu trả lời của bạn, điều này sẽ rất tuyệt. Giả sử bạn đang theo dõi việc sử dụng một số tài nguyên thông qua một BlockingCollection. Chúng được sử dụng (và bị xóa khỏi bộ sưu tập) và quay trở lại bộ sưu tập khi chúng có sẵn một lần nữa để tái sử dụng. Việc tắt máy chỉ có thể xảy ra khi tất cả những thứ này đã trở lại bộ sưu tập. Làm thế nào bạn sẽ đi về báo hiệu rằng một tắt máy có thể đi trước (tức là bộ sưu tập là đầy đủ một lần nữa) khác hơn là chờ đợi bận rộn? – Anastasiosyal

+0

@Anastasiosyal Tôi sẽ đóng gói bộ sưu tập và có trình bao bọc làm điều gì đó khi đầy; thực tế, tôi có thể sử dụng một 'Monitor' cho điều này (sẽ thêm ví dụ) –

+0

Trong destructor của một lớp 'ObjectPool' xuống từ BlockingQueue, pop tất cả các đối tượng từ BlockingCollection từng cái một và vứt bỏ chúng, (vì đây là C#, đặt tham chiếu đã truy xuất về nil), cho đến khi số lượng đối tượng xuất hiện bằng độ sâu của hồ bơi. Tất cả các đối tượng gộp lại sau đó đã được xử lý, vì vậy bạn có thể vứt bỏ hàng đợi và trở về từ dtor để tiếp tục trình tự tắt máy. Không yêu cầu bỏ phiếu/bận rộn! Bạn có thể muốn đặt một số thời gian chờ trên mất để một đối tượng bị rò rỉ cuối cùng tăng một ngoại lệ, (hoặc một cái gì đó). –

97

Trong .NET 4 SpinWait thực hiện CPU-chuyên sâu quay vòng 10 lần lặp lại trước khi sinh. Nhưng nó không trả lại cho người gọi ngay lập tức sau mỗi chu kỳ đó; thay vào đó, nó gọi Thread.SpinWait để quay qua CLR (chủ yếu là hệ điều hành) trong một khoảng thời gian đã định.Khoảng thời gian này ban đầu là vài chục nano giây nhưng tăng gấp đôi với mỗi lần lặp cho đến khi 10 lần lặp hoàn tất. Điều này cho phép rõ ràng/khả năng dự đoán trong tổng thời gian quay vòng (CPU-chuyên sâu), mà hệ thống có thể điều chỉnh theo điều kiện (số lõi, vv). Nếu SpinWait vẫn còn trong giai đoạn sinh lợi kéo dài quá lâu, nó sẽ định kỳ ngủ để cho phép các luồng khác tiếp tục (xem J. Albahari's blog để biết thêm thông tin). Quá trình này được đảm bảo để giữ một lõi bận rộn ...

Vì vậy, SpinWait giới hạn quay CPU-chuyên sâu cho một số bộ lặp, sau đó nó mang lại lát thời gian của mình trên tất cả quay (bằng cách thực sự kêu gọi Thread.YieldThread.Sleep) , giảm mức tiêu thụ tài nguyên của nó. Nó cũng sẽ phát hiện nếu người dùng đang chạy một máy tính lõi đơn và sinh lời trên mỗi chu kỳ nếu đúng như vậy.

Với Thread.Sleep chủ đề bị chặn. Nhưng quá trình này sẽ không đắt như ở trên về mặt CPU.

+6

+1 Cảm ơn sự rõ ràng giữa SpinWait và thread.Sleep – Anastasiosyal

+0

Điều này được sao chép từ trang web của Albahari. Nó nên được tham chiếu! – georgiosd

+1

Nó không được sao chép đúng nguyên văn, nhưng đã bị ảnh hưởng bởi trang web của ông vì vậy tại sao tôi đã tham chiếu blog của ông. – MoonKnight

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