ThreadPool.QueueUserWorkItem和异步编程

时间:2010-09-17 03:57:44

标签: multithreading

我在下面写了一个示例程序。

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调用Count方法时,我注意到Main在Count方法完成之前完成,而Count2方法与Count Method纠结在一起。

Main(和Count2)是否还要等到Count方法完成?我不想使用锁也不想使用Thread.Sleep(因为我不知道Count操作需要多长时间)。我已经读过某处异步调用或WAIT用于这段时间。

有什么想法吗?

2 个答案:

答案 0 :(得分:4)

我将介绍两种模式。这两种模式都具有高度可扩展性,因为它们可以处理数百甚至数千个同步工作项。您必须完全遵循模式。任何偏差都可能导致事件信号与等待事件之间的竞争状况。我已经在循环的上下文中呈现了模式以概括它,但在您的情况下,您将使用对ThreadPool.QueueUserWorkItem的两次单独调用替换循环。

第一个模式需要CountdownEvent类,它可以在.NET 4.0中使用,也可以作为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 Framework 1 。它虽然不那么优雅。

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();

1 当然,如果使用更早的版本,你必须用匿名方法替换lambda表达式,甚至是真正的方法。

答案 1 :(得分: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();
    }
}