2010-03-29 47 views

Trả lời

51

Bất cứ khi nào bạn muốn kiểm soát việc thực hiện nhiều luồng trong ứng dụng của mình. Mặc dù điều này không chỉ có nghĩa là chỉ có một luồng tăng bộ đếm; nhưng để cho các chủ đề bắt đầu/dừng hoặc tạm dừng khi một sự kiện.

Xem WaitHandles - Auto/ManualResetEvent and Mutex

--EDIT--

WaitHandle s là cơ chế mà bạn "sử dụng" để kiểm soát thực hiện của chủ đề. Không phải của nó về xử lý không thể truy cập trong một chủ đề; về việc sử dụng chúng trong chuỗi.

Đây có thể là ví dụ về chất béo, nhưng vui lòng chịu với tôi; suy nghĩ về, một người phụ nữ đưa năm chiếc còi săn chắc khác nhau cho năm cô gái, và bảo họ thổi còi bất cứ khi nào something sẽ xảy ra; quá trình này là để mỗi cô gái thổi còi, và người phụ nữ sẽ biết ai thổi còi.

Bây giờ, nó không về việc chia sẻ-the-còi với nhau, nó về, có lẽ đối với người phụ nữ, sử dụng chúng để "kiểm soát" việc chấp hành hoặc quá trình như thế nào cô gái sẽ thổi tiếng còi.

Do đó, về mặt kỹ thuật quá trình này sẽ được:

  1. Tạo một sự kiện chờ đợi (ManualResetEvent đối tượng)
  2. đăng ký các sự kiện, WaitHandle.WaitAny(events);
  3. Sau khi bạn đã hoàn thành hoạt động biểu diễn trong chủ đề của bạn, .Set() , sẽ nói với WaitHandle rằng 'Tôi đã hoàn tất!'.

Ví dụ: hãy xem xét ví dụ từ liên kết được cung cấp. Tôi đã thêm các bước để bạn hiểu logic. Đây không phải là các bước được mã hóa cứng, nhưng chỉ để bạn có thể hiểu được.

class Test 
{ 
    static void Main() 
    { 
    //STEP 1: Create a wait handle 
     ManualResetEvent[] events = new ManualResetEvent[10];//Create a wait handle 
     for (int i=0; i < events.Length; i++) 
     { 
      events[i] = new ManualResetEvent(false); 
      Runner r = new Runner(events[i], i); 
      new Thread(new ThreadStart(r.Run)).Start(); 
     } 

    //STEP 2: Register for the events to wait for 
     int index = WaitHandle.WaitAny(events); //wait here for any event and print following line. 

     Console.WriteLine ("***** The winner is {0} *****", 
          index); 

     WaitHandle.WaitAll(events); //Wait for all of the threads to finish, that is, to call their cooresponding `.Set()` method. 

     Console.WriteLine ("All finished!"); 
    } 
} 


class Runner 
{ 
    static readonly object rngLock = new object(); 
    static Random rng = new Random(); 

    ManualResetEvent ev; 
    int id; 

    internal Runner (ManualResetEvent ev, int id) 
    { 
     this.ev = ev;//Wait handle associated to each object, thread in this case. 
     this.id = id; 
    } 

    internal void Run() 
    { 
    //STEP 3: Do some work 
     for (int i=0; i < 10; i++) 
     { 
      int sleepTime; 
      // Not sure about the thread safety of Random... 
      lock (rngLock) 
      { 
       sleepTime = rng.Next(2000); 
      } 
      Thread.Sleep(sleepTime); 
      Console.WriteLine ("Runner {0} at stage {1}", 
           id, i); 
     } 

    //STEP 4: Im done! 
     ev.Set(); 
    } 
} 
+0

WaitAll() - được sử dụng để chờ cho tất cả các tay cầm trong một bộ được tự do/tín hiệu ... Có nghĩa là Handles sẽ không thể truy cập vào các chủ đề khác cho đến khi chúng không được phát hành (các chủ đề khác sẽ đợi cho các tay cầm trong bộ này)? – DotNetBeginner

+1

@DotNetBeginner: 'Điều đó có nghĩa là Handles sẽ không truy cập được vào các chủ đề khác'; vui lòng xem bài đăng cập nhật của tôi, một ví dụ được thêm vào chỉ để trả lời câu hỏi này. –

+0

nó là giá trị nói rằng 'ev.Set()' tốt hơn để được đặt trong 'cuối cùng 'khối –

6

Ý tưởng đằng sau các phương pháp WaitAll và WaitAny là chúng hữu ích khi bạn có nhiều tác vụ mà bạn muốn chạy song song.

Ví dụ: giả sử bạn phải thực hiện công việc yêu cầu bạn thực hiện một số quy trình cho 1000 mục trong một mảng cần được xử lý song song. Một điển hình Core 2 Duo + siêu phân luồng chỉ có 4 bộ vi xử lý hợp lý, và do đó, nó không có ý nghĩa để có nhiều hơn 4 chủ đề cùng một lúc (thực sự, nó, nhưng đó là một câu chuyện cho một thời gian khác - chúng tôi sẽ giả vờ và sử dụng mô hình "một luồng trên mỗi bộ xử lý" đơn giản bây giờ). Vì vậy, 4 chủ đề, nhưng 1000 mục; bạn làm nghề gì?

Một tùy chọn là sử dụng phương pháp WaitAny. Bạn khởi động 4 chủ đề, và mỗi khi phương thức WaitAny trả về bạn bắt đầu một chuỗi khác, cho đến khi tất cả 1000 mục được xếp hàng đợi. Lưu ý rằng đây là một ví dụ kém cho WaitAny, vì bạn cũng có thể chia mảng của bạn thành 250 khối mục. Hy vọng rằng, mặc dù, nó cung cấp cho bạn và ý tưởng về loại tình huống mà WaitAny là hữu ích. Có những tình huống tương tự khác, nơi WaitAny có thể có nhiều ý nghĩa.

Nhưng bây giờ chúng ta hãy quay trở lại kịch bản với 4 chủ đề mà mỗi quá trình 250 mục từ mảng 1000 mục của bạn. Với tùy chọn này, bạn có thể sử dụng phương thức WaitAll để chờ tất cả quá trình xử lý kết thúc.

7

Đây là lớp trừu tượng, bạn không sử dụng trực tiếp. Các lớp dẫn xuất bê tông là ManualResetEvent, AutoResetEvent, Mutex và Semaphore. Các lớp quan trọng trong hộp công cụ của bạn để thực hiện đồng bộ hóa luồng. Họ kế thừa các phương thức WaitOne, WaitAll và WaitAny, bạn sử dụng chúng để phát hiện rằng một hoặc nhiều luồng đã báo hiệu điều kiện chờ đợi.

Kịch bản sử dụng điển hình cho thủ công/AutoResetEvent là cho biết một chuỗi để thoát hoặc để cho một chuỗi tín hiệu rằng nó đã tiến tới một điểm chuỗi quan trọng. Semaphore giúp bạn giới hạn số lượng các chủ đề thực hiện một hành động. Hoặc để thực hiện đồng bộ hóa luồng không nên có ái lực với một chuỗi cụ thể. Mutex có quyền gán quyền sở hữu cho một phần mã cho một luồng, câu lệnh khóa thường được áp dụng ở đó.

Sách đã được viết về nó. Joe Duffy's Concurrent Programming in Windows là mới nhất và tuyệt vời nhất. Rất khuyến khích nếu bạn chiêm ngưỡng việc viết mã chuỗi.

27

WaitHandle là lớp cơ sở trừu tượng cho hai trình xử lý sự kiện thường được sử dụng: AutoResetEventManualResetEvent.

Cả hai lớp này đều cho phép một chuỗi "báo hiệu" một hoặc nhiều luồng khác. Chúng được sử dụng để đồng bộ hóa (hoặc tuần tự hóa hoạt động) giữa các luồng. Điều này được thực hiện bằng các phương pháp SetWaitOne (hoặc WaitAll). Ví dụ:

Chủ đề 1:

// do setup work 

myWaitHandle.Set(); 

Chủ đề 2:

// do setup work 

myWaitHandle.WaitOne(); 

// this code will not continue until after the call to `Set` 
// in thread 1 completes. 

Đây là một ví dụ rất thô sơ, và có vô số chúng có sẵn trên web.Ý tưởng cơ bản là WaitOne được sử dụng để chờ tín hiệu từ một chuỗi khác cho biết rằng điều gì đó đã xảy ra. Trong trường hợp của AsyncWaitHandle (được trả lại từ cách gọi một đại biểu không đồng bộ), WaitOne cho phép bạn làm cho luồng hiện tại phải chờ cho đến khi thao tác không đồng bộ hoàn tất.

Khi số AutoResetEvent hoặc ManualResetEvent không được đặt, các cuộc gọi đến WaitOne sẽ chặn chuỗi cuộc gọi đến khi số Set được gọi. Hai lớp này chỉ khác nhau trong trường hợp AutoResetEvent "bỏ chọn" sự kiện này khi cuộc gọi thành công đến WaitOne hoàn tất, thực hiện lại các cuộc gọi tiếp theo cho đến khi Set được gọi. ManualResetEvent phải được "bỏ đặt" một cách rõ ràng bằng cách gọi Reset.

WaitAllWaitAny là các phương pháp tĩnh trên lớp WaitHandle cho phép bạn chỉ định mảng WaitHandles để chờ. WaitAll sẽ chặn cho đến khi tất cả của các tay cầm được cung cấp là Set, trong khi WaitAny sẽ chỉ chặn cho đến một trong số đó là Set.

1

Có một số câu trả lời rất dài tại đây.Đối với bất kỳ ai tìm kiếm câu trả lời ngắn:

Đợi xử lý chờ đợi là cơ chế để chờ một luồng cho đến khi một chuỗi khác đạt đến một điểm nhất định.

Bạn cũng có thể có một số chuỗi đang chờ và/hoặc một số chuỗi đang chờ đợi, do đó các phương thức WaitOne, WaitAllWaitAny. Ngoài ra còn có một số tùy chọn cho ngữ nghĩa có sẵn bằng cách chọn một trong các lớp sau: Mutex, Semaphore, ManualResetEvent, AutoResetEvent cũng được ghi lại.

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