代码改变世界

.net 4.0新特性-CountDownEvent

2010-06-10 12:56  Clingingboy  阅读(1365)  评论(0编辑  收藏  举报

也称为反Semaphore 作用:统计其他线程结束工作,监听计数为0时,触发操作.
与Semaphore形成对比,Semaphore有最大资源数,当计数为0时(即资源不够用时)则阻塞线程
1.Semaphore
初始化时,资源为0,调用Release方法释放资源

using System;
using System.Threading;

public class Example
{
    // A semaphore that simulates a limited resource pool.
    //
    private static Semaphore _pool;

    // A padding interval to make the output more orderly.
    private static int _padding;

    public static void Main()
    {
        // Create a semaphore that can satisfy up to three
        // concurrent requests. Use an initial count of zero,
        // so that the entire semaphore count is initially
        // owned by the main program thread.
        //
        _pool = new Semaphore(0, 3);

        // Create and start five numbered threads. 
        //
        for (int i = 1; i <= 5; i++)
        {
            Thread t = new Thread(new ParameterizedThreadStart(Worker));

            // Start the thread, passing the number.
            //
            t.Start(i);
        }

        // Wait for half a second, to allow all the
        // threads to start and to block on the semaphore.
        //
        Thread.Sleep(500);

        // The main thread starts out holding the entire
        // semaphore count. Calling Release(3) brings the 
        // semaphore count back to its maximum value, and
        // allows the waiting threads to enter the semaphore,
        // up to three at a time.
        //
        Console.WriteLine("Main thread calls Release(3).");
        _pool.Release(3);

        Console.WriteLine("Main thread exits.");
    }

    private static void Worker(object num)
    {
        // Each worker thread begins by requesting the
        // semaphore.
        Console.WriteLine("Thread {0} begins " +
            "and waits for the semaphore.", num);
        _pool.WaitOne();

        // A padding interval to make the output more orderly.
        int padding = Interlocked.Add(ref _padding, 100);

        Console.WriteLine("Thread {0} enters the semaphore.", num);

        // The thread's "work" consists of sleeping for 
        // about a second. Each thread "works" a little 
        // longer, just to make the output more orderly.
        //
        Thread.Sleep(1000 + padding);

        Console.WriteLine("Thread {0} releases the semaphore.", num);
        Console.WriteLine("Thread {0} previous semaphore count: {1}",
            num, _pool.Release());
    }
}
可以看到同时进入三个线程
image

2.CountDownEvent
初始化当前线程计数即为1,然后在其他线程运行前调用AddCount进行计数,执行完毕后调用Signal方法减少计数.等所有线程计数后,还需要在当前线程中调用Signal,等所有工作完成计数为0时,Wait方法将不再阻塞
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

class CancelableCountdowEvent
{
    class Data
    {
        public int Num { get; set; }
        public Data(int i) { Num = i; }
        public Data() { }
    }

    class DataWithToken
    {
        public CancellationToken Token { get; set; }
        public Data Data { get; private set; }
        public DataWithToken(Data data, CancellationToken ct)
        {
            this.Data = data;
            this.Token = ct;
        }
    }
    static IEnumerable<Data> GetData()
    {
        return new List<Data>() { new Data(1), new Data(2), new Data(3), new Data(4), new Data(5) };
    }
    static void ProcessData(object obj)
    {
        DataWithToken dataWithToken = (DataWithToken)obj;
        if (dataWithToken.Token.IsCancellationRequested)
        {
            Console.WriteLine("Canceled before starting {0}", dataWithToken.Data.Num);
            return;
        }

        for (int i = 0; i < 1000; i++)
        {
            // Increase this value to slow down the program.
            Thread.SpinWait(100000);
        }
        Console.WriteLine("Processed {0}", dataWithToken.Data.Num);
    }

    static void Main(string[] args)
    {
        EventWithCancel();

        Console.WriteLine("Press enter to exit.");
        Console.ReadLine();
    }

    static void EventWithCancel()
    {
        IEnumerable<Data> source = GetData();
        CancellationTokenSource cts = new CancellationTokenSource();

        //Enable cancellation request from a simple UI thread.
        Task.Factory.StartNew(() =>
        {
            if (Console.ReadKey().KeyChar == 'c')
                cts.Cancel();
        });

        // Event must have a count of at least 1
        CountdownEvent e = new CountdownEvent(1);


        // fork work:
        foreach (Data element in source)
        {
            DataWithToken item = new DataWithToken(element, cts.Token);
            // Dynamically increment signal count.
            e.AddCount();
            ThreadPool.QueueUserWorkItem(delegate(object state)
            {
                ProcessData(state);
                //if (!cts.Token.IsCancellationRequested)
                e.Signal();
            },
             item);
        }
        //// Decrement the signal count by the one we added
        //// in the constructor.
        e.Signal();

        //// The first element could be run on this thread.

        //// Join with work or catch cancellation.
        try
        {
            e.Wait(cts.Token);
        }
        catch (OperationCanceledException oce)
        {
            if (oce.CancellationToken == cts.Token)
            {
                Console.WriteLine("User canceled.");
            }
            else throw; //We don't know who canceled us!
        }
        e.Dispose();

        //... 
    } //end method
} //end class

 

参考:

http://www.cnblogs.com/shanyou/archive/2009/10/27/1590890.html
http://blogs.msdn.com/b/pfxteam/archive/2008/06/21/8629821.aspx

http://msdn.microsoft.com/zh-cn/magazine/cc163427.aspx