zoukankan      html  css  js  c++  java
  • 多线程编程学习笔记——线程同步(三)

    接上文 多线程编程学习笔记——线程同步(一)

    接上文 多线程编程学习笔记——线程同步(二)

    七、使用Barrier类

    Barrier类用于组织多个线程及时在某个时刻会面,其提供一个回调函数,每次线程调用了SignalAndWait方法后该回调函数就会被执行。

    1.代码如下:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading; //引入线程
    using System.Diagnostics;
     
    
    namespace ThreadSynchronousDemo
    {
        class Program
        {
            static Barrier barr = new Barrier(2,b=>Console.WriteLine(" 第 {0} 阶段结束",b.CurrentPhaseNumber+1));       
    
            static void Main(string[] args)
            {
                Console.WriteLine("开始,Barrier 同步");   
    
                var t = new Thread((() => working("线程 1 ", "第 1 个工作线程任务", 3)));
                var t2 = new Thread((() => working("线程 2","第 2 个工作线程任务", 6)));   
    
                t.Start();
                t2.Start();     
    
                Console.Read();
            }
    
            static void working(string name,string message,int seconds)
            {
    
                for (int i = 0; i < 3; i++)
                {
                    Console.WriteLine("--------工作阶段-------------");
                    Thread.Sleep(TimeSpan.FromSeconds(seconds));
                    Console.WriteLine("{0} 开始工作,内容:{1}",name,message);
    
                    Thread.Sleep(TimeSpan.FromSeconds(seconds));
                    Console.WriteLine("{0} 的工作时间总计{1} 。结束工作,{2}", name,seconds, message);
                    barr.SignalAndWait();
                }     
            }
        }
    }

    2.运行结果。如下图。

     

              创建了一个Barrier实例,指定了想要同步的两个线程,两个线程任何一个线程调用了SignalAndWait方法之后,都会调用回调函数打印出当前的处于什么阶段。这个类在多线程迭代计算中非常有用,可以在每个迭代结束之前执行一些操作,当最后一个线程调用SignalAndWait方法时,在迭代结束之前进行交互。

     

    八、使用ReaderWriteLockSlim类

    ReaderWriteLockSlim代表了一个管理资源访问的锁,允许多个线程同时读取,但中独占写。

     1.代码如下。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading; //引入线程
    using System.Diagnostics;
     
    
    namespace ThreadSynchronousDemo
    {
        class Program
        {
            static ReaderWriterLockSlim rws = new ReaderWriterLockSlim();
            static List<int> list = new List<int>();     
    
            static void Main(string[] args)
            {
                Console.WriteLine("开始,ReaderWriterLockSlim 同步");      
    
                var t = new Thread(Read) { IsBackground=true};
                var t2 = new Thread(Read) { IsBackground = true };
                var t3 = new Thread(Read) { IsBackground = true };
     
    
                var t4 = new Thread((() => Write("线程 1 "))) { IsBackground = true };
                var t5 = new Thread((() => Write("线程 2"))) { IsBackground = true };
                //读取数据线程
                t.Start();
                t2.Start();
                t3.Start();
                //写入数据线程
                t4.Start();
                t5.Start();
                Console.Read();
            } 
    
            static void Read()
            {
                Console.WriteLine("--------从List中读取数据-------------");
                while (true)
                {
                    try
                    {
                        rws.EnterReadLock();
                        foreach (var item in list)
                        {
                            Console.WriteLine("读取数据内容:{0}", item);
                            Thread.Sleep(TimeSpan.FromSeconds(1));
                        }
                    }
                    catch (Exception ex)
                    {
                        throw ex;
                    }
                    finally
                    {
                        rws.ExitReadLock();
                    }         
                } 
            }
    
            static void Write(string threadName)
            {
                Console.WriteLine("--------往List中写数据-------------");
                while (true)
                {
                    try
                    {
                        int newInt = new Random().Next(1, 100);
                        rws.EnterUpgradeableReadLock();
                        if(!list.Contains(newInt))
                        {
                            //如果List中没有该数据,则写入
                            try
                            {
                                rws.EnterWriteLock();
                                list.Add(newInt);
                                Console.WriteLine("{0} 写入数据内容:{1}", threadName,newInt);
                            }
                            finally
                            {
                                rws.ExitWriteLock();
                            }                     
    
                            Thread.Sleep(TimeSpan.FromSeconds(1));                    }
                    }
                    catch (Exception ex)
                    {
                        throw ex;
                    }
                    finally
                    {
                        rws.ExitUpgradeableReadLock();
                    }
                }
    
            }
        }
    
    }

    2.程序运行结果,发下图。

     

           程序启动后,直接运行五个线程,其中三个线程从集合中读取数据,二个线程往集合中写数据。

          读锁允许多线程同时读取数据,写锁则在释放之前阻塞了其余的所有线程的操作。当线程获取了读锁,从集合中读取数据时,还要判断当前集合上是否有写锁要写数据,如果有写锁则会阻塞线程进行读取数据,从而会浪费时间。所以本例中使用了EnterUpgradeabledReadLock和ExitUpgradeabledReadLock方法,先获取读锁,然后读数据,如果要修改集合中的数据,则把锁升级成ReaderWriterLock然后进行写数据,最后 使用ExitWriteLock退出锁。

     

    九、使用SpinWait类

     SpinWait是一个混合同步构造,即先使用用户模式等待一段时间,然后切换到内核模式,以节省CPU时间。

      1.代码如下

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading; //引入线程
    using System.Diagnostics; 
    
    namespace ThreadSynchronousDemo
    {
        class Program
        {
            static volatile bool isCompleted=false;       
    
            static void Main(string[] args)
            {
                Console.WriteLine("开始,SpinWait 同步");      
    
                var t = new Thread(UserModeWait);
                var t2 = new Thread(HybirdSpinWait) ;
                Console.WriteLine("开始,运行用户模式");
    
                t.Start();
                Thread.Sleep(50);
                isCompleted = true;
                Thread.Sleep(TimeSpan.FromSeconds(5));
                isCompleted = false;
                Console.WriteLine("开始,运行内核模式");
                t2.Start();
    
                Thread.Sleep(TimeSpan.FromSeconds(5));
                isCompleted = true;
                Console.Read();
            }
     
    
            static void UserModeWait()
            {
                Console.WriteLine("--------用户模式 等待-------------");
                while (!isCompleted)
                {
                            Console.Write("");
                            Thread.Sleep(TimeSpan.FromSeconds(1));   
                }
    
                Console.WriteLine();
                Console.WriteLine("用户模式 等待结束");
            }
    
            static void HybirdSpinWait()
            {
                var spin = new SpinWait();
                Console.WriteLine("--------内核模式-------------");
                while (!isCompleted)
                {
    
                    spin.SpinOnce();
                   // NextSpinWillYield:其决定了调用SpinOnce方法的线程是否应该让出CPU
                    Console.WriteLine("是否应该让出CPU :{0}",spin.NextSpinWillYield);
                }         
    
                Console.WriteLine("内核模式 结束");
            }
        }
    }

    2.程序运行结果,如下2图。

     

     

          程序中我们有一个执行无限循环的线程,在50ms之后主线程会设置isCompleted为true。我们可以在windows资源管理器,查看CPU的负载情况。

  • 相关阅读:
    阿里消息队列中间件 RocketMQ 源码分析 —— Message 拉取与消费(上)
    数据库中间件 ShardingJDBC 源码分析 —— SQL 解析(三)之查询SQL
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 解析(六)之删除SQL
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 解析(五)之更新SQL
    消息队列中间件 RocketMQ 源码分析 —— Message 存储
    源码圈 300 胖友的书单整理
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 路由(一)分库分表配置
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 解析(四)之插入SQL
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 路由(二)之分库分表路由
    C#中Math类的用法
  • 原文地址:https://www.cnblogs.com/chillsrc/p/7778416.html
Copyright © 2011-2022 走看看