2010-09-17 56 views
1

我已經在下面寫了一個示例程序。ThreadPool.QueueUserWorkItem和異步編程

class Program 
    { 
     static int x = 2; 

     static void Main(string[] args) 
     { 
      Console.WriteLine("Thread ID {0} and Main Called!", Thread.CurrentThread.ManagedThreadId); 

      ThreadPool.QueueUserWorkItem(Count, args); 
      ThreadPool.QueueUserWorkItem(Count2, args); 

      Console.WriteLine("Thread ID {0} and Main Done!", Thread.CurrentThread.ManagedThreadId); 
      Console.ReadLine(); 
     } 
     static void Count(object args) 
     { 
      for (int i = 0; i < 10; i++) 
      { 
       x = x + 2; 
       Console.WriteLine("Thread ID {0} AND Count 1: " + x, Thread.CurrentThread.ManagedThreadId); 
      } 
     } 
     static void Count2(object args) 
     { 
      for (int i = 0; i < 10; i++) 
      { 
       x = x + 2; 
       Console.WriteLine("Thread ID {0} AND Count 2: " + x, Thread.CurrentThread.ManagedThreadId); 
      } 
     } 
    } 

當調用使用ThreadPool.QueueUserWorkItem的計數方法,我注意到計數方法完成,共2個記錄方法被糾結了計數法之前主要完成。

Main(和Count2)有沒有等到Count方法完成後?我不想使用鎖或Thread.Sleep(因爲我不知道Count操作需要多長時間)。我在某處讀過異步呼叫,或者在這段時間使用WAIT。

任何想法?

+0

如果Count2必須在Count之後發生,爲什麼將它們排隊爲單獨項目? – 2010-09-17 04:05:05

+0

什麼是替代方案?你會如何排隊呢? – Mage 2010-09-17 04:06:48

回答

1

我覺得AutoResetEvent是你所追求的

**編輯:** 下面是修改後的代碼(未經測試)。我在這個例子中使用了AutoResetEvent而不是Manual

class Program 
{ 
    static int x = 2; 
    // Define an array with two AutoResetEvent WaitHandles. 
    static WaitHandle[] waitHandles = new WaitHandle[] 
    { 
     new AutoResetEvent(false), 
     new AutoResetEvent(false) 
    }; 



    static void Main(string[] args) 
    { 
     Console.WriteLine("Thread ID {0} and Main Called!", Thread.CurrentThread.ManagedThreadId); 

     ThreadPool.QueueUserWorkItem(new WaitCallback(Count), waitHandles[0]); 
     ThreadPool.QueueUserWorkItem(new WaitCallback(Count2), waitHandles[1]); 
     WaitHandle.WaitAll(waitHandles); 


     Console.WriteLine("Thread ID {0} and Main Done!", Thread.CurrentThread.ManagedThreadId); 
     Console.ReadLine(); 
    } 
    static void Count(object args) 
    { 
     AutoResetEvent are = (AutoResetEvent)args; 

     for (int i = 0; i < 10; i++) 
     { 
      x = x + 2; 
      Console.WriteLine("Thread ID {0} AND Count 1: " + x, Thread.CurrentThread.ManagedThreadId); 
     } 
     are.Set(); 

    } 
    static void Count2(object args) 
    { 
     AutoResetEvent are = (AutoResetEvent)args; 

     for (int i = 0; i < 10; i++) 
     { 
      x = x + 2; 
      Console.WriteLine("Thread ID {0} AND Count 2: " + x, Thread.CurrentThread.ManagedThreadId); 
     } 
     are.Set(); 
    } 
} 
4

我將介紹兩種模式。這兩種模式都具有高度的可擴展性,因爲它們可以處理數百甚至數千個同時發生的工作項目。你必須嚴格遵循模式。任何偏差都可能導致事件發生和等待事件之間的競爭狀態。我已經在一個循環的上下文中展示了這些模式來推廣它,但在你的情況下,你會用兩個單獨的調用替換循環到ThreadPool.QueueUserWorkItem

第一種模式需要在.NET 4.0中提供的CountdownEvent類,或者作爲Reactive Extensions下載的一部分。

var finished = new CountdownEvent(1); 
for (int i = 0; i < NUM_WORK_ITEMS; i++) 
{ 
    finished.AddCount(); 
    ThreadPool.QueueUserWorkItem(
    (state) => 
    { 
     try 
     { 
     // Your work item code goes here. 
     // Call Count, Count2, or whatever. 
     } 
     finally 
     { 
     finished.Signal(); 
     } 
    }); 
} 
finished.Signal(); 
finished.WaitOne(); 

下面的模式將與.NET框架任何版本。它雖然不是很優雅。

int count = 1; 
var finished = new ManualResetEvent(false); 
for (int i = 0; i < NUM_WORK_ITEMS; i++) 
{ 
    Interlocked.Increment(ref count); 
    ThreadPool.QueueUserWorkItem(
    (state) => 
    { 
     try 
     { 
     // Your work item code goes here. 
     // Call Count, Count2, or whatever. 
     } 
     finally 
     { 
     if (Interlocked.Decrement(ref count) == 0) finished.Set(); 
     } 
    }); 
} 
if (Interlocked.Decrement(ref count) == 0) finished.Set(); 
finished.WaitOne(); 

當然,你將不得不與匿名方法或者如果使用更早版本甚至一個真正的方法來代替lambda表達式。

+0

upvote爲模式2.偉大的作品。謝謝。 – Vince 2010-10-29 20:09:17