zoukankan      html  css  js  c++  java
  • .NET基础拾遗(7)多线程开发基础4

    一.多线程编程中的线程同步

        1.C#中的lock关键字

      lock关键字可能是我们在遇到线程同步的需求时最常用的方式,但lock只是一个语法糖,为什么这么说呢,下面慢慢道来。

      (1)lock的等效代码其实是Monitor类的Enter和Exit两个方法

    private object locker = new object();
        public void Work()
        {
              lock (locker)
              {
                  // 做一些需要线程同步的工作
              }
         }
    private object locker = new object(); public void Work() { // 避免直接使用私有成员locker(直接使用有可能会导致线程不安全) object temp = locker; Monitor.Enter(temp); try { // 做一些需要线程同步的工作 } finally { Monitor.Exit(temp); } }

         (2)System.Threading.Monitor类型的作用和使用

      Monitor类型的Enter和Exit方法用来实现进入和退出对象的同步,当Enter方法被调用时,对象的同步索引将被检查,并且.NET将负责一系列的后续工作来保证对象访问时的线程同步,而Exit方法的调用则保证了当前线程释放该对象的同步块。

      示例演示了如何使用lock关键字来实现线程同步:

    class Program
        {
            static void Main(string[] args)
            {
                // 多线程测试静态方法的同步
                Console.WriteLine("开始测试静态方法的同步:");
                for (int i = 0; i < 5; i++)
                {
                    Thread thread = new Thread(Lock.StaticIncrement);
                    thread.Start();
                }
                // 这里等待线程执行结束
                Thread.Sleep(5 * 1000);
                Console.WriteLine("-------------------------------");
                // 多线程测试实例方法的同步
                Console.WriteLine("开始测试实例方法的同步:");
                Lock l = new Lock();
                for (int i = 0; i < 6; i++)
                {
                    Thread thread = new Thread(l.InstanceIncrement);
                    thread.Start();
                }
    
                Console.ReadKey();
            }
        }
    
        public class Lock
        {
            // 静态方法同步锁
            private static object staticLocker = new object();
            // 实例方法同步锁
            private object instanceLocker = new object();
    
            // 成员变量
            private static int staticNumber = 0;
            private int instanceNumber = 0;
    
            // 测试静态方法的同步
            public static void StaticIncrement(object state)
            {
                lock (staticLocker)
                {
                    Console.WriteLine("当前线程ID:{0}", Thread.CurrentThread.ManagedThreadId.ToString());
                    Console.WriteLine("staticNumber的值为:{0}", staticNumber.ToString());
                    // 这里可以制造线程并行执行的机会,来检查同步的功能
                    Thread.Sleep(200);
                    staticNumber++;
                    Console.WriteLine("staticNumber自增后为:{0}", staticNumber.ToString());
                }
            }
    
            // 测试实例方法的同步
            public void InstanceIncrement(object state)
            {
                lock (instanceLocker)
                {
                    Console.WriteLine("当前线程ID:{0}",Thread.CurrentThread.ManagedThreadId.ToString());
                    Console.WriteLine("instanceNumber的值为:{0}", instanceNumber.ToString());
                    // 这里可以制造线程并行执行的机会,来检查同步的功能
                    Thread.Sleep(200);
                    instanceNumber++;
                    Console.WriteLine("instanceNumber自增后为:{0}", instanceNumber.ToString());
                }
            }
        }
    

      

    PS: 应该完全避免用this对象和当前类型对象作为同步对象,而是在类型中定义私有的同步对象,

    同时应该使用lock而不是Monitor类型,这样可以有效地减少同步块不被释放的情况。

    2. 互斥体是个什么鬼?Mutex和Monitor两个类型的功能有啥区别?

      (1)什么是互斥体?

      在操作系统中,互斥体(Mutex)是指某些代码片段在任意时间内只允许一个线程进入。例如,正在进行一盘棋,任意时刻只允许一个棋手往棋盘上落子,这和线程同步的概念基本一致。

      (2).NET中的互斥体

      Mutex类是.NET中为我们封装的一个互斥体类型,和Mutex类似的还有Semaphore(信号量)等类型。下面的示例代码展示了Mutext类型的使用

    class Program
        {
            const string testFile = "C:\TestMutex.txt";
            /// <summary>
            /// 这个互斥体保证所有的进程都能得到同步
            /// </summary>
            static Mutex mutex = new Mutex(false, "TestMutex");
    
            static void Main(string[] args)
            {
                //留出时间来启动其他进程
                Thread.Sleep(3000);
                DoWork();
                mutex.Close();
                Console.ReadKey();
            }
    
            /// <summary>
            /// 往文件里写连续的内容
            /// </summary>
            static void DoWork()
            {
                long d1 = DateTime.Now.Ticks;
                mutex.WaitOne();
                long d2 = DateTime.Now.Ticks;
                Console.WriteLine("经过了{0}个Tick后进程{1}得到互斥体,进入临界区代码。", (d2 - d1).ToString(), Process.GetCurrentProcess().Id.ToString());
    
                try
                {
                    if (!File.Exists(testFile))
                    {
                        FileStream fs = File.Create(testFile);
                        fs.Dispose();
                    }
                    for (int i = 0; i < 5; i++)
                    {
                        // 每次都保证文件被关闭再重新打开
                        // 确定有mutex来同步,而不是IO机制
                        using (FileStream fs = File.Open(testFile, FileMode.Append))
                        {
                            string content = "【进程" + Process.GetCurrentProcess().Id.ToString() +
                                "】:" + i.ToString() + "
    ";
                            Byte[] data = Encoding.Default.GetBytes(content);
                            fs.Write(data, 0, data.Length);
                        }
                        // 模拟做了其他工作
                        Thread.Sleep(300);
                    }
                }
                finally
                {
                    mutex.ReleaseMutex();
                }
            }
        } 

    模拟多个用户,执行上述代码,下图就是在我的计算机上的执行结果:

      现在打开C盘目录下的TestMutext.txt文件,将看到如下图所示的结果:

          

    (3)Mutex和Monitor的区别

      这两者虽然都用来进行同步的功能,但实现方法不同,其最显著的两个差别如下:

      ① Mutex使用的是操作系统的内核对象,而Monitor类型的同步机制则完全在.NET框架之下实现,这就导致了Mutext类型的效率要比Monitor类型要低很多

      ② Monitor类型只能同步同一应用程序域中的线程,而Mutex类型却可以跨越应用程序域和进程

  • 相关阅读:
    WPF使用Mutex创建单实例程序失效
    招式百出的数组(js)
    js中,如何把一个混杂的字符串进行去重并按数字在左,字母在右组成的新字符串打印出来
    字符串对象(js)
    时间对象(js)
    数组迭代的5个小宝贝(js)
    线程的状态
    线程
    JAVA学习规划
    简单的反射实例
  • 原文地址:https://www.cnblogs.com/tiantianle/p/5932312.html
Copyright © 2011-2022 走看看