zoukankan      html  css  js  c++  java
  • 深入浅出多线程系列之五:一些同步构造(上篇)

    1:Mutex

    Mutex 就像一个C# lock一样,不同的是它可以跨进程.

    进入和释放一个Mutex要花费几毫秒,大约比C#lock50倍。

    使用一个Mutex的实例,调用WaitOne方法来获取锁,ReleaseMutex方法来释放锁。

    因为Mutex是跨进程的,所以我们可以使用Mutex来检测程序是否已经运行。

            public static void MainThread()
            {
                
    using (var mutex = new Mutex(false"LoveJenny OneAtATimeDemo"))
                {
                    
    if (!mutex.WaitOne(TimeSpan.FromSeconds(3), false))
                    {
                        Console.WriteLine(
    "只能运行一个应用程序!");
                        
    return;
                    }

                    RunProgram();
                }
            }

    2:Semaphore:

    一个Semaphore就像一个酒吧一样,通过门卫来限制它的客人,一旦到达限制,没有人可以进入,

    人们会在门外乖乖的排队,一旦有一个人离开酒吧,排队中的人就可以进入了一个了。

    下面是个例子:

        class TheClub
        {
           
    //只能容纳三个人的酒吧
            static SemaphoreSlim _sem = new SemaphoreSlim(3);

            
    public static void MainThread()
            {
                
    for (int i = 1; i <= 5; i++)
                    
    new Thread(Enter).Start(i); //有5个人向进入
            }
            
    static void Enter(object id)
            {
                Console.WriteLine(id 
    + " 想要进入了");
                _sem.Wait();
                Console.WriteLine(id
    +" 已经进入了!");
                Thread.Sleep(
    1000 * (int)id);
                Console.WriteLine(id 
    + " 离开了?");
                _sem.Release();
            }
        }

    3:AutoResetEvent

    一个AutoResetEvent就像十字转门一样,插入一张票就让一个人通过,”Auto”代表门会自动的关上。

    在十字门外面的人可以调用WaitOne方法来阻塞,等待。一旦有人插入了票(调用Set方法),就可以让外面等待的人(调用WaitOne方法的线程)通过了。

    创建AutoResetEvent有一个参数。

    static EventWaitHandle _waitHandle = new AutoResetEvent(false);

    其中falsemsdn的解释是:初始状态为非终止,

    按照我个人的理解false代表了十字转门非终止,所以可以正常的进入,等待。

    而如果是true的话:初始状态为终止,也就是代表已经调用了Set了,

    就是说十字转门已经停止了,所以接下来如果有人调用了WaitOne方法,这个调用WaitOne方法的人直接就可以进入了,不需要再插入票(不需要调用Set)了,之后的调用和false一致,这一点可以认为AutoResetEvent具有记忆功能,它记住了上次门是打开的状态。所以调用waitone方法可以进入。

    class ThreadAutoResetEvent
        {
            
    static EventWaitHandle _waitHandle = new AutoResetEvent(false);

            
    public static void MainThread()
            {
                
    new Thread(Waiter).Start();
                Thread.Sleep(
    2000);
                _waitHandle.Set();
            }

            
    static void Waiter()
            {
                Console.WriteLine(
    "Waiting...");
                _waitHandle.WaitOne();
                Console.WriteLine(
    "Notified");
            }
    }

    很简单,Waiter执行到Waiting…后,就开始调用WaitOne了,所以在门外排队等待。

    而主线程在睡了两秒后,开始插入一张票(Set).所以Waiter就继续执行,所以打印Notified

     

     

    接下来我们使用AutoResetEvent来模拟实现生产消费问题:

    class ProducerConsumerQueue:IDisposable
        {
            EventWaitHandle _wh 
    = new AutoResetEvent(false);
            Thread _worker;
            
    readonly object _locker = new object();
            Queue
    <string> _tasks = new Queue<string>();

            
    public ProducerConsumerQueue()
            {
                
    //创建并启动工作线程
                _worker = new Thread(Work);
                _worker.Start();
            }

            
    public void EnqueueTask(string task)
            {
                
    lock (_locker) _tasks.Enqueue(task);
                _wh.Set(); 
    //一旦有任务了,唤醒等待的线程
            }

            
    public void Dispose()
            {
                EnqueueTask(
    null);
                _worker.Join(); 
    //等待_worker线程执行结束
                _wh.Close();
            }

            
    void Work()
            {
                
    while (true)
                {
                    
    string task = null;
                    
    lock (_locker)
                    {
                        
    if (_tasks.Count > 0
                        {
                            task 
    = _tasks.Dequeue();
                            
    if (task == null)
                                
    return;
                        }
                        
    if (task != null//如果有任务的话,执行任务
                        {
                            Console.WriteLine(
    "Performing task: " + task);
                            Thread.Sleep(
    1000);
                        }
                        
    else //否则阻塞,去睡觉吧
                        {
                            _wh.WaitOne();
                        }
                    }
                }
            }
        }

    主线程调用如下:

            public static void Main()
            {
                
    using (ProducerConsumerQueue q = new ProducerConsumerQueue())
                {
                    q.EnqueueTask(
    "Hello");
                    
    for (int i = 0; i < 10; i++) q.EnqueueTask("Say " + i);
                    q.EnqueueTask(
    "Goodbye!");
                }
            }

    4:ManualResetEvent

    一个ManualResetEvent就是一个普通门,

    调用Set方法门就打开了,允许任意数量的人进入。

    调用WaitOne方法就开始等待进入。

    调用Reset方法门就关闭了。

    在一个关闭的门上调用WaitOne方法就会被阻塞。

    当门下次被打开的时候,所有等待的线程都可以进入了。

    除了这些不同外,一个ManualResetEventAutoResetEvent类似。

    Framework4.0ManualResetEvent提供了一个优化版本。ManualResetEventSlim。后面的版本速度更快,并且支持取消(CancellationToken).

     

     

    参考资料:

    http://www.albahari.com/threading/

    CLR Via C# 3.0

  • 相关阅读:
    有关.net 框架的学习笔记
    简单定义工程架构
    respondsToSelector的相关使用
    IOS框架和服务
    iOS常用第三方类库
    ios换肤思想,及工具类
    集成激光推送
    远程推送
    ios本地推送
    UIPopoverController 的使用
  • 原文地址:https://www.cnblogs.com/LoveJenny/p/2053677.html
Copyright © 2011-2022 走看看