• .NET 4中的多线程编程之三:共享数据(下)


    上文介绍的互斥方法都是用于进程间的不同线程的互斥。.NET 提供了不同进程间线程互斥的方法。可以使用named mutex来实现进程间的互斥。named mutex是一个全局的mutex,通过给mutex一个名称,可以在所有进程间有效。注意要仔细选择mutex的名称,避免和其他程序冲突。

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    namespace TaskParallel
    {
        class MainClass
        {
            public static void Main(string[] args)
            {
                string mutexName = "Parallel.MainClass";
                Mutex globalMutex;
                try
                {
                    globalMutex = Mutex.OpenExisting(mutexName);
                }
                catch (WaitHandleCannotBeOpenedException)
                {
                    globalMutex = new Mutex(false, mutexName);
                }
                Task t = new Task(() =>
                {
                    while (true)
                    {
                        Console.WriteLine("Waiting to acquire Mutex");
                        globalMutex.WaitOne();
                        Console.WriteLine("Acquired.Press Enter to release");
                        Console.ReadLine();
                        globalMutex.ReleaseMutex();
                        Console.WriteLine("Released");
                    }
                });
                t.Start();
                t.Wait();
            }
        }
    }

    首先使用Mutex.OpenExist方法来判断是否已经存在该命名mutex,如果不存在,则会抛出一个异常,再使用Mutex(bool,string)构造函数创建新的mutex,第一个参数如果为true,那么这个mutex初始情况下就已经被锁,需要调用mutex.Release()之后才能使用。Mutex还有一个构造函数,可以避免抛出异常,

    bool created;
    Mutex globalMutex = new Mutex(false, mutexName, out created);

    created如果是false,则说明当前命名mutex已经存在。编译之后打开两次这个程序,可以看到两个进程依次获得mutex锁:

    image

    提个外话,我在linux下配置成功了Mono 2.10.2 和 MonoDevelop 2.8,用的发行版是OpenSUSE,之前尝试过过Ubuntu和Debian,由于没有MonoDevelop的最新的package,不支持.NET4,自己编译安装遇到一系列的问题,linux下装软件除非有现成的安装包,否则几乎必然是一场灾难。OpenSUSE非常的好用,和Mono同宗同源,支持的很好。MonoDevelop自然不能和VS2010比,不过也足够用了。

    screenshot

    我用Mono编译运行这个named mutex程序,编译没有问题,但是运行起来完全不是期望的结果。进程间没有互斥。

    Screenshot-1_副本

    不知道是不是Mono的bug,在网上查了下,也没有查到。进程间互斥本来就用的不多,再加上是Mono,用的人更少了。标记一下。

    言归正传,下面介绍 concurrent collections。通常都通过集合类型来共享(传递)数据,如果要在多个线程中同时访问集合对象,也要考虑互斥的问题,否则会发生错误,例如:

    using System;
    using System.Collections.Generic;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace Parallel
    {
        public class ConcurrentCollection
        {
            public static void Main(string[] args)
            {
                Queue<int> sharedQueue = new Queue<int>();
                for (int i = 0; i < 10000; i++)
                    sharedQueue.Enqueue(i);
                int itemCount = 0;
                Task[] tasks = new Task[10];
                for (int i = 0; i < 10; i++)
                {
                    tasks[i] = new Task(() =>
                    {
                        while (sharedQueue.Count > 0)
                        {
                            int item = sharedQueue.Dequeue();
                            Interlocked.Increment(ref itemCount);
                        }
                    });
                    tasks[i].Start();
                }
                Task.WaitAll(tasks);
                Console.WriteLine("{0} items processed", itemCount);
            }
        }
    }

    输出的结果往往会大于1000,而且有时会因异常退出。这是因为Dequeue方法不是原子操作,可能会执行到一半被打断,使得Queue 内部的状态被打乱.

    PS.在Mono下此程序执行的结果完全确定,每次都和预设的itemCount一致。看来Mono的行为和MS的.NET还是有差别。

    .NET 4提供了一组可以并发访问的集合类型,以System.Collections.Concurrent.ConcurrentQueue 为例,它的Enqueue和TryDequeue方法都是线程安全的,TryDequeue方法如果成功取得了对象,就返回true,否则返回false,因此上面的代码可以改成:

    while (sharedQueue.Count > 0)
    {
           int item;
           while (!sharedQueue.TryDequeue(out item)) ;                      
           Interlocked.Increment(ref itemCount);
    }

    这样就可以安全访问了.

    PS. 很不理解.net 为什么把Enqueue和Dequeue设计成如此不对称的方式。查看了下Mono的源代码,它的TryDequeue似乎永远也不会返回false,也就是说本质上是和Enqueue对称的。我试验了下,确实不会返回false。而微软的实现就不一样,会大量的返回false,必需用一个while套起来不断循环,比较奇怪。下次要继续研究下这样设计的原因。

  • 相关阅读:
    关于QTTabBar的使用
    Delay, Sleep, Pause, & Wait in JavaScript
    highly recommended javascript books
    How to get the current script element
    MDX格式的字典制作
    while循环体的新用法
    printf函数的标准表达式
    C语言生成随机数代码
    C语言数组代码,小明摘苹果
    几个简单常用的C语言函数
  • 原文地址:https://www.cnblogs.com/yinzixin/p/2250562.html
Copyright © 2020-2023  润新知