2010-09-17 75 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