2012-08-25 32 views
5

http://www.codeproject.com/Articles/28785/Thread-synchronization-Wait-and-Pulse-demystifiedMonitor.Pulse & Chờ - Hành vi bất ngờ

Queues:

Hàng đợi sẵn sàng là bộ sưu tập các chủ đề đang chờ đợi một khóa đặc biệt . Các phương thức Monitor.Wait giới thiệu một hàng đợi khác: hàng chờ đợi . Điều này là bắt buộc khi đợi Pulse khác biệt khi chờ để lấy khóa. Giống như hàng đợi sẵn sàng, hàng chờ đợi là FIFO.

Mẫu được đề xuất:

Những hàng đợi này có thể dẫn đến hành vi không mong muốn. Khi xảy ra xung, người đứng đầu hàng đợi đang chờ được phát hành và được thêm vào hàng đợi sẵn sàng . Tuy nhiên, nếu có các chủ đề khác trong hàng đợi sẵn sàng, chúng sẽ lấy khóa trước khi luồng được giải phóng. Đây là một vấn đề , bởi vì các chủ đề mà mua lại các khóa có thể làm thay đổi trạng thái mà các chủ đề xung dựa vào. Giải pháp là sử dụng một điều kiện trong khi trong tuyên bố khóa

* Q = Hàng đợi.

Bằng cách đó, tôi hiểu rằng khi tôi gọi Pulse, nó sẽ làm 2 việc trước khi kết thúc. Thứ nhất, nó loại bỏ một sợi từ Q chờ đợi đến Q. sẵn sàng Thứ hai, nó cho phép một luồng (mà không biết ai là luồng đó) trong Ready Q có khóa; nó không quan tâm ai mua lại khóa (chủ đề đến từ sự chờ đợi Q hoặc một chủ đề trong Q sẵn sàng vì một lý do nào đó).

Nếu tôi đúng về điều đó thì tại sao được đặt một while trước Monitor.Wait giúp khắc phục vấn đề (vấn đề - xung kết thúc ngay cả khi chủ đề đến từ chờ đợi Q không có được các khóa)?

A. Hãy cho tôi biết nếu tôi đúng về mục đích của Monitor.Pulse.

B. Tại sao tôi cần phải đặt một while trước Monitor.Wait

Mã đầy đủ các câu trả lời dưới đây:

class Program 
{ 
    static Queue<int> queue = new Queue<int>(); 
    static object someMonitor = new object(); 

    static void Main(string[] args) 
    { 
     Thread Thread1 = new Thread(WorkAlltheTime); 
     Thread1.Name = "Thread1"; 
     Thread Thread2 = new Thread(WorkAlltheTime); 
     Thread2.Name = "Thread2"; 
     Thread Thread3 = new Thread(WorkOnce); 
     Thread3.Name = "Thread3"; 
     Thread1.Start(); 
     Thread2.Start(); 
     Thread.Sleep(1000); 
     Thread3.Start(); 
     Console.ReadLine(); 
    } 

    static void WorkAlltheTime() 
    { 
     Console.WriteLine("Came in to Ready Q: " + Thread.CurrentThread.Name); 
     lock (someMonitor) 
     { 
      Console.WriteLine("Came out from Ready Q: " + Thread.CurrentThread.Name); 
      // Broken! 
      while (queue.Count == 0) 
      { 
       Console.WriteLine("Came in to Waiting Q: " + Thread.CurrentThread.Name); 
       Monitor.Wait(someMonitor); 
       Console.WriteLine("Came out from Waiting Q: " + Thread.CurrentThread.Name); 
      } 
      queue.Dequeue(); 
      Console.WriteLine("Thread: "+Thread.CurrentThread.Name+" Pulled Out"); 
     } 
    } 

    static void WorkOnce() 
    { 
     lock (someMonitor) 
     { 
      queue.Enqueue(1); 
      Monitor.Pulse(someMonitor); 
     } 
    } 
} 

Trả lời

9

Hãy tưởng tượng bạn đang cố gắng để viết một hàng đợi nhà sản xuất/tiêu dùng - bạn Pulse mỗi khi bạn tạo ra một mặt hàng và người tiêu dùng cần đợi cho đến khi có một vật phẩm để tiêu thụ. Bạn muốn viết mã như thế này:

Foo item; 
lock(someMonitor) 
{ 
    while (queue.Count == 0) 
    { 
     Monitor.Wait(someMonitor); 
    } 
    item = queue.Dequeue(); 
} 
// Use the item 

Giả sử bạn không có vòng lặp while, và thay vào đó đã viết:

Foo item; 
lock(someMonitor) 
{ 
    // Broken! 
    if (queue.Count == 0) 
    { 
     Monitor.Wait(someMonitor); 
    } 
    item = queue.Dequeue(); 
} 
// Use the item 

Bây giờ giả sử bạn có một thread đã chờ đợi, và sau đó một chủ đề ngay trước khi tuyên bố khóa ... sau đó một nhà sản xuất xung màn hình (và thêm một mục vào hàng đợi, tất nhiên).

Tại thời điểm đó, hoàn toàn khả thi là luồng mà thậm chí chưa có khóa sẽ là người đầu tiên để khóa khóa ... tại thời điểm đó, khi chuỗi "chờ đợi" mua lại khóa, hàng đợi sẽ trống một lần nữa. Chỉ với một tuyên bố if duy nhất, không có vòng lặp, bạn sẽ kết thúc khử nhiễu khi hàng đợi trống, điều này sẽ thất bại.

Với vòng lặp while, bạn sẽ đợi lần nữa cho đến khi mục tiếp theo được tạo ra, đó là những gì bạn thực sự muốn.

+0

Cảm ơn bạn, tôi đã viết mã đầy đủ của ví dụ của bạn và với lời giải thích của bạn tôi đã thử nghiệm nó 90 lần và tôi hoàn toàn hiểu ngay bây giờ. Cảm ơn Agian! –

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