2009-04-27 29 views
11

Đây không phải là về các phương pháp khác nhau mà tôi có thể hoặc nên sử dụng để sử dụng hàng đợi theo cách tốt nhất, thay vào đó tôi đã thấy điều gì đó không có ý nghĩa với tôi.C# Luồng và Hàng đợi

void Runner() { 
    // member variable 
    queue = Queue.Synchronized(new Queue()); 
    while (true) { 
     if (0 < queue.Count) { 
      queue.Dequeue(); 
     } 
    } 
} 

này được chạy trong một chủ đề duy nhất:

var t = new Thread(Runner); 
t.IsBackground = true; 
t.Start(); 

sự kiện khác là "Enqueue" ing những nơi khác. Những gì tôi đã thấy xảy ra là trong một khoảng thời gian, Dequeue sẽ thực sự ném InvalidOperationException, hàng đợi rỗng. Điều này sẽ không thể nhìn thấy như thế nào số lượng đảm bảo có cái gì đó ở đó, và tôi tích cực rằng không có gì khác là "Dequeue" ing.

Câu hỏi (s):

  1. Có thể rằng Enqueue thực sự làm tăng số lượng trước khi mặt hàng đó là hoàn toàn vào hàng đợi (theo đúng nghĩa ...)?
  2. Có thể chủ đề đang khởi động lại bằng cách nào đó (hết hạn, đặt lại ...) tại câu lệnh Dequeue, nhưng ngay sau khi nó đã xóa một mục?

Chỉnh sửa (làm rõ):

Những mảnh đang là một phần của một lớp Wrapper mà thực hiện các chủ đề nền helper. Dequeue ở đây là Dequeue duy nhất, và tất cả Enqueue/Dequeue là trên biến thành viên được đồng bộ (hàng đợi).

+0

Vì câu trả lời của Ryan ... đây có phải là mã thực hay chỉ là một ví dụ đơn giản? Nếu nó là mã thực sự, bạn thực sự nên suy nghĩ về việc thay đổi vòng lặp - bỏ phiếu hàng đợi thay vì đồng bộ hóa người đọc với các nhà văn là một thiết kế kém. Bạn đang lãng phí hàng triệu chu trình xử lý để làm nóng phòng. –

+0

Đây là một ví dụ để có được thịt của vấn đề. Có một Thread.Sleep trong đó, bộ xử lý không bị búa. Lý do tôi chọn cho một quá trình bỏ phiếu thay vì đồng bộ đọc/nhà văn là do thực tế là hàng đợi có một cái gì đó trong nó gần như tất cả các thời gian. Trong thân cây của chúng tôi mặc dù tôi đã thêm một AutoResetEvent để chơi xung quanh với. Như tôi đã nói ở trên cùng, tôi không lo lắng về việc triển khai ở đây. Có vẻ như là một vấn đề chính hãng với mô hình luồng này, có thể là đúng hay sai. – neouser99

+0

Từ khi xem mã của bạn, ít nhất bạn cũng có chủ đề chính và chuỗi mà bạn cho rằng Dequeue đang được gọi. Tại sao không đặt tên cho các chủ đề của bạn, và mỗi khi Dequeue được gọi, hãy ghi tên của luồng bằng một dấu vết ngăn xếp. Bạn có thể thấy rằng một cái gì đó trong chủ đề chính là hành xử theo cách bạn không mong đợi. –

Trả lời

11

Sử dụng Trình phản xạ, bạn có thể thấy rằng không, số lượng không tăng lên cho đến khi mục được thêm vào.

Như Ben chỉ ra, có vẻ như bạn có nhiều người gọi dequeue.

Bạn nói rằng bạn tích cực là không có gì khác gọi là dequeue. Có phải bởi vì bạn chỉ có một chủ đề gọi là dequeue? Là dequeue gọi là bất cứ nơi nào khác ở tất cả?

CHỈNH SỬA:

Tôi đã viết một mã mẫu nhỏ, nhưng không thể khắc phục sự cố. Nó chỉ tiếp tục chạy và chạy mà không có bất kỳ ngoại lệ nào.

Bao lâu nó chạy trước khi bạn gặp lỗi? Có thể bạn có thể chia sẻ thêm một chút mã.

class Program 
{ 
    static Queue q = Queue.Synchronized(new Queue()); 
    static bool running = true; 

    static void Main() 
    { 
     Thread producer1 = new Thread(() => 
      { 
       while (running) 
       { 
        q.Enqueue(Guid.NewGuid()); 
        Thread.Sleep(100); 
       } 
      }); 

     Thread producer2 = new Thread(() => 
     { 
      while (running) 
      { 
       q.Enqueue(Guid.NewGuid()); 
       Thread.Sleep(25); 
      } 
     }); 

     Thread consumer = new Thread(() => 
      { 
       while (running) 
       { 
        if (q.Count > 0) 
        { 
         Guid g = (Guid)q.Dequeue(); 
         Console.Write(g.ToString() + " "); 
        } 
        else 
        { 
         Console.Write(" . "); 
        } 
        Thread.Sleep(1); 
       } 
      }); 
     consumer.IsBackground = true; 

     consumer.Start(); 
     producer1.Start(); 
     producer2.Start(); 

     Console.ReadLine(); 

     running = false; 
    } 
} 
+0

Rất đẹp, tôi thực sự có một trường hợp thử nghiệm trông khá gần với điều này, và nó có được ý chính. Dịch vụ này đã hoạt động được khoảng 2 tuần trước khi tôi gặp sự cố. – neouser99

+8

Tại thời điểm này, tất cả những gì tôi có thể đề nghị là: (1) Bạn đã nhập phần If, (2) Một Solar Flare lật bit của bạn về không, (3) Hàng đợi của bạn đã ném một ngoại lệ. Nó thực sự nên đã được ném một SolarFlareException, nhưng bất cứ điều gì. Potatoh, Potahto. –

3

Dưới đây là những gì tôi nghĩ rằng chuỗi vấn đề là:

  1. (0 < queue.Count) trả về true, hàng đợi là không có sản phẩm nào.
  2. Chuỗi này được preempted và một chuỗi khác chạy.
  3. Chủ đề khác sẽ xóa một mục khỏi hàng đợi, làm trống nó.
  4. Chủ đề này tiếp tục thực hiện, nhưng bây giờ là trong khối nếu và cố gắng để dequeue một danh sách trống.

Tuy nhiên, bạn nói không có gì khác là dequeuing ...

Hãy thử outputting đếm bên trong nếu khối. Nếu bạn nhìn thấy số lượng nhảy số xuống, một người khác là dequeuing.

+2

Đây cũng là suy nghĩ đầu tiên của tôi, nhưng ông nói "Tôi rất tích cực rằng không có gì khác là" Dequeue "ing." – BFree

+0

Hãy suy nghĩ về những suy nghĩ của tôi, nhưng tôi nghĩ anh ấy nên làm sáng tỏ sự thật khiến anh ta chắc chắn rằng không có gì khác gọi là Dequeue. –

+0

+1. Đó là tình trạng cuộc đua giữa Count và Dequeue bất kể có bất kỳ chủ đề nào khác là * hiện tại * dequeuing hay không. Tốt nhất để khắc phục tình trạng cuộc đua và chuyển sang vấn đề thực sự. –

3

Dưới đây là một câu trả lời có thể từ the MSDN page về chủ đề này:

Tiến hành thống kê thông qua một bộ sưu tập được về bản chất không phải là một thread-safe thủ tục. Ngay cả khi một bộ sưu tập là được đồng bộ, các chủ đề khác vẫn có thể sửa đổi bộ sưu tập, điều này gây ra điều tra viên để ném ngoại lệ. Để đảm bảo an toàn luồng trong khi điều chỉnh , bạn có thể khóa bộ sưu tập trong toàn bộ số điều tra hoặc bắt ngoại lệ do các thay đổi được thực hiện bởi các chủ đề khác.

Tôi đoán là bạn đã đúng - tại một thời điểm nào đó, có tình trạng chạy đua xảy ra và cuối cùng bạn sẽ loại bỏ điều gì đó không có ở đó.

Một Mutex hoặc Monitor.Lock có thể thích hợp ở đây.

Chúc may mắn!

2

Các khu vực khác có dữ liệu "Enqueuing" cũng sử dụng cùng một đối tượng hàng đợi được đồng bộ hóa không? Để Queue.Synchronized được thread-safe, tất cả các hoạt động Enqueue và Dequeue phải sử dụng cùng một đối tượng hàng đợi được đồng bộ hóa.

Từ MSDN:

Để đảm bảo an toàn thread của Queue, mọi hoạt động phải được thực hiện thông qua chỉ wrapper này.

được sửa đổi: Nếu bạn đang Looping trên nhiều mặt hàng có liên quan đến tính toán nặng hoặc nếu bạn đang sử dụng một vòng lặp chủ đề dài hạn (thông tin liên lạc, vv), bạn nên xem xét việc có một chức năng chờ đợi như System.Threading.Thread.Sleep, System.Threading.WaitHandle.WaitOne , System.Threading.WaitHandle.WaitAll, hoặc System.Threading.WaitHandle.WaitAny trong vòng lặp, nếu không nó có thể giết hiệu năng hệ thống.

+0

"[...] hãy chắc chắn rằng bạn có System.Threading.Thread.Sleep (1) trong bất kỳ vòng lặp thread [...]"!?! Gì? Tại sao bạn nên làm một cái gì đó như thế? –

+0

Ahhh ... hiểu rồi. Ý bạn là while (true) {}. Và không - bạn thực sự không nên thêm Thread.Sleep() vào vòng lặp này. Bạn không nên thêm Thread.Sleep() vào bất kỳ vòng lặp nào. Đây chỉ là thiết kế rất kém và cần được cố định - luồng đọc phải đồng bộ hóa với luồng ghi thay vì bỏ chọn hàng đợi. –

+1

Chế độ ngủ cho phép hệ thống chia sẻ CPU và tài nguyên nếu không luồng sẽ tiêu thụ tài nguyên quan trọng (trong một số trường hợp sẽ bỏ đói các luồng và quy trình khác). Rõ ràng đây là mã mẫu, nhưng có một lúc (đúng) không có điều kiện thoát nên tôi đã đề cập đến điều này. Giấc ngủ (0) có vấn đề không từ bỏ quyền kiểm soát đối với các chuỗi ưu tiên thấp hơn, vì vậy nên sử dụng chế độ Ngủ (1). Nếu vòng lặp luồng được định thời gian hoặc có khoảng thời gian giới hạn, thì có thể không cần thiết để Ngủ. Tôi đã nhìn thấy một số dịch vụ Windows trong đó một lệnh Sleep duy nhất thay đổi mức sử dụng CPU từ gần 80-100% xuống <1%. – Ryan

1

câu hỏi 1: Nếu bạn đang sử dụng hàng đợi được đồng bộ hóa, thì: không, bạn an toàn! Nhưng bạn sẽ cần sử dụng thể hiện được đồng bộ hóa ở cả hai bên, nhà cung cấp và bộ nạp.

câu hỏi 2: Chấm dứt chuỗi công nhân của bạn khi không có việc phải làm, là một công việc đơn giản. Tuy nhiên, bạn cần một chuỗi giám sát hoặc có hàng đợi bắt đầu một chuỗi công nhân nền bất cứ khi nào hàng đợi có thứ gì đó cần làm. Cái cuối cùng nghe có vẻ giống như mẫu ActiveObject hơn là một hàng đợi đơn giản (đó là một mẫu đơn-Responsibily-Pattern nói rằng nó chỉ nên xếp hàng).

Ngoài ra, tôi muốn tìm hàng đợi chặn thay vì mã của bạn ở trên. Cách mã của bạn hoạt động đòi hỏi sức mạnh xử lý CPU ngay cả khi không có công việc để làm. Hàng đợi chặn cho phép chuỗi công nhân của bạn ngủ bất cứ khi nào không có gì để làm. Bạn có thể có nhiều luồng ngủ đang chạy mà không cần sử dụng sức mạnh xử lý CPU.

C# không đi kèm với việc thực thi hàng đợi chặn, nhưng có nhiều thứ ở đó. Xem số điện thoại example này và one này.

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