zoukankan      html  css  js  c++  java
  • 多线程-3(同步)

    SemaphoreSlim类

    代码:

    static SemaphoreSlim semaphoreSlim = new SemaphoreSlim(4);//只允许4个并行执行的线程
            public  static void AccessDataBase(string name,int seconde)
            {
                Console.WriteLine("{0} 等待访问数据库", name);
                semaphoreSlim.Wait();
                Console.WriteLine("{0} 授予对数据库的访问权", name);
                Thread.Sleep(TimeSpan.FromSeconds(seconde));
                Console.WriteLine("{0} 完成", name);
                semaphoreSlim.Release();
            }
            static void Main(string[] args)
            {
                for (int i = 0; i < 6; i++)
                {
                    string threadName = "Thread" + i;
                    int secondsToWait = 2 + 2 * i;
                    var t = new Thread(() => AccessDataBase(threadName, secondsToWait));
                    t.Start();
                }
            }
    View Code

     主线程启动,创建SemaphoreSlim的一个实例,在构造函数中指定允许并发线程数量,启动6个不同名称和不同初始运行时间的线程,借助信号系统限制访问数据的并发数, 只允许4个线程获取, 注意:.当线程1完成后,线程0才进行授权访问。

    AutoResetEven类

      private static AutoResetEvent workResetEvent = new AutoResetEvent(false);
            private static AutoResetEvent mainRestEvent = new AutoResetEvent(false);
    
            static void Process(int seconds)
            {
                Console.WriteLine("开始一项长期的工作");
                Thread.Sleep(TimeSpan.FromSeconds(seconds));
                Console.WriteLine("work执行完毕");
                workResetEvent.Set();
                Console.WriteLine("等待主线程完成它的工作");
                mainRestEvent.WaitOne();
                Console.WriteLine("开始第二次操作");
                Thread.Sleep(TimeSpan.FromSeconds(seconds));
                Console.WriteLine("工作完成");
                workResetEvent.Set();
            }
            static void Main(string[] args)
            {
                var t = new Thread(() => Process(10));
                t.Start();
                workResetEvent.WaitOne();
                Console.WriteLine("第一次操作完成");
                Console.WriteLine("在第二个线程上执行操作");
                Thread.Sleep(TimeSpan.FromSeconds(5));
                mainRestEvent.Set();
                Console.WriteLine("现在在第二个线程上运行第二个操作");
                workResetEvent.WaitOne();
                Console.WriteLine("第二个线程执行完毕");
            }
    View Code

    AutoResetEvent类采用的是内核时间模式,所以等待时间不能太长。使用ManualResetEventslim类更好。

    ManualResetEventSlim类

     static ManualResetEventSlim mainEvent = new ManualResetEventSlim(false);
            static void TravleThroughGates(string threadName,int seconds)
            {
                Console.WriteLine("{0} falls to sleep",threadName);
                Thread.Sleep(TimeSpan.FromSeconds(seconds));
                Console.WriteLine("{0} waits for  the gates to open!",threadName);
                mainEvent.Wait();//
                Console.WriteLine("{0} enters the gates!", threadName);
            }
            static void Main(string[] args)
            {
                var t1 = new Thread(() => TravleThroughGates("threadName 1", 5));
                var t2 = new Thread(() => TravleThroughGates("threadName 2", 6));
                var t3 = new Thread(() => TravleThroughGates("threadName 3", 12));
                t1.Start();
                t2.Start();
                t3.Start();
                Thread.Sleep(TimeSpan.FromSeconds(6));
                Console.WriteLine("The gates are now open!");
                mainEvent.Set();//
                Thread.Sleep(TimeSpan.FromSeconds(2));
                mainEvent.Reset();//将事件状态设置为非终止,从而导致线程受阻
                Console.WriteLine("The gates have been closed!");
                Thread.Sleep(TimeSpan.FromSeconds(10));
                Console.WriteLine("The gates are now open for the seconds time!");
                mainEvent.Set();
                Thread.Sleep(TimeSpan.FromSeconds(2));
                Console.WriteLine("The gates have been closed!");
                mainEvent.Reset();
    
            }

    这里启动了三个线程,同时进行等待。ManualResetEventSlim 的set是允许准备好的线程接受信号并继续工作。

    CountDownEvent类

    CountDownEvent信号类来等待直到一定数量的操作完成。

    static CountdownEvent countdownEvent = new CountdownEvent(2);
            static void PerformOperation(string message,int seconds)
            {
                Thread.Sleep(TimeSpan.FromSeconds(seconds));
                Console.WriteLine(message);
                countdownEvent.Signal();
            }
            static void Main(string[] args)
            {
                Console.WriteLine("Starting two operations");
                var t1 = new Thread(() => PerformOperation("Operation 1 is completd", 4));
                var t2 = new Thread(() => PerformOperation("Operation 2 is completd", 8));
                t1.Start();
                t2.Start();
                countdownEvent.Wait();
                Console.WriteLine("Both operation have been completd");
                countdownEvent.Dispose();
            }

    启动两个线程,当它们执行完成后会发生信号。一旦第二个线程完成,主程序会从等待CountdownEvent的状态中返回并继续执行。当针对需要等待多个异步操作的情形,方法非常遍历。当然如果某个线程出现死锁等情况,一直等待下,那么会一直等待。

    Barrier类

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

     static Barrier barrier = new Barrier(2, b => Console.WriteLine("End of  phase {0}", b.CurrentPhaseNumber + 1));
            static void PlayMusic(string name,string messages,int seconds)
            {
                for (int i = 0; i < 3; i++)
                {
                    Console.WriteLine("-----------------------------");
                    Thread.Sleep(TimeSpan.FromSeconds(seconds));
                    Console.WriteLine("{0} starts to {1}", name, messages);
                    Thread.Sleep(TimeSpan.FromSeconds(seconds));
                    Console.WriteLine("{0} finshes to {1}", name, messages);
                    barrier.SignalAndWait();
                }
            }
            static void Main(string[] args)
            {
                var t1 = new Thread(() => PlayMusic("the guitarist", "play an amazing solo", 5));
                var t2 = new Thread(() => PlayMusic("the singer", "sing his song", 2));
                t1.Start();
                t2.Start();
    
            }

    使用Barrier,指定我们想同步两个线程。这两个线程任意一个调用了SignalAndWait方法后,会执行一个回调来打印出阶段

     ReaderWriterLockSlim

    ReaderWriterLockSlim类来创建一个线程安全的机制,在多线程中对一个集合进行读写操作。ReaderWriterLockSlim代表一个管理资源访问的锁,允许多个线程同时读取,以及独占写。

    static ReaderWriterLockSlim readerWriterLockSlim = new ReaderWriterLockSlim();
            static Dictionary<int, int> items = new Dictionary<int, int>();
            static void Read()
            {
                Console.WriteLine("Reader Contexts of a dictionary");
                while(true)
                {
                    try
                    {
                        readerWriterLockSlim.EnterReadLock();
                        foreach (var key in items.Keys)
                        {
                            Thread.Sleep(TimeSpan.FromSeconds(0.1));
                        }
                    }
                    finally
                    {
                        readerWriterLockSlim.ExitReadLock();
                    }
                }
            }
            static void Writer(string threadName)
            {
                while(true)
                {
                    try
                    {
                        int newkey = new Random().Next(250);
                        readerWriterLockSlim.EnterUpgradeableReadLock();
                        if(!items.ContainsKey(newkey))
                        {
                            try
                            {
                                readerWriterLockSlim.EnterWriteLock();
                                items[newkey] = 1;
                                Console.WriteLine("New key {0} is added to  a  dictionary by a {1}",newkey, threadName);
                            }
                            finally
                            {
                                readerWriterLockSlim.ExitWriteLock();
                            }
                        }
                        Thread.Sleep(TimeSpan.FromSeconds(0.1));
                    }
                    catch (Exception)
                    {
    
                        readerWriterLockSlim.ExitUpgradeableReadLock();
                    }
                }
            }
            static void Main(string[] args)
            {
                new Thread(Read) { IsBackground = true }.Start();
                new Thread(Read) { IsBackground = true }.Start();
                new Thread(Read) { IsBackground = true }.Start();
    
                new Thread(() => Writer("Thread 1")){ IsBackground = true }.Start();
                new Thread(() => Writer("Thread 2")) { IsBackground = true }.Start();
                Thread.Sleep(TimeSpan.FromSeconds(30));
            }
    View Code

    同时运行了三个程序来从字典中读取数据,还有另外两个线程向该字典中写入数据,使用ReaderWriterLockSlim类实现线程安全。

    这里使用两种锁:读锁允许多线程读取数据,写锁在被释放前会阻塞了其他线程的所有操作,先获取读锁后读取数据,如果发现必须修改底层集合,只需使用EnterWriterLock方法升级锁,然后快速执行写操作。

    SpanWait类

    如果不使用内核模型的方式来使线程等待。SpanWait是一个混合同步结构,设计为使用用户模式等待一段时间,然后切换到内核模式以节省CPU时间

     static volatile bool isCompleted = false;//volatile允许多个线程访问,呈现最新的
            static void UserModelWait()
            {
                while(!isCompleted)
                {
                    Console.WriteLine(".");
                }
                Console.WriteLine("Waiting is complete");
            }
            static void HybridSpinWait()
            {
                var w = new SpinWait();
                while (!isCompleted)
                {
                    w.SpinOnce();
                    Console.WriteLine(w.NextSpinWillYield);
                }
                Console.WriteLine("Waiting is complete");
            }
            static void Main(string[] args)
            {
                var t1 = new Thread(UserModelWait);
                var t2 = new Thread(HybridSpinWait);
    
                Console.WriteLine("Running user mode waiting");
                t1.Start();
                Thread.Sleep(20);
                isCompleted = true;
                Thread.Sleep(TimeSpan.FromSeconds(1));
                isCompleted = false;
                Console.WriteLine("Running hybrid SpinWait construct waiting");
                Thread.Sleep(5);
                isCompleted = true;
            }

    定义一个线程,将执行一个无止境的循环,直到20毫秒后主线程设置isCompleted变量为true,我们可以试验周期为20-30秒,通过windows任务管理器测量CPU的负载情况。

    用volatie关键字来声明iscompleted静态字段,volatie字段不会被编辑器和处理器优化,只能被单个线程访问。

     
  • 相关阅读:
    微信小程序开发之tab导航栏
    微信小程序开发之日期组件
    微信小程序运行机制
    iis部署错误:HTTP 错误 500.21
    web API请求与参数获取
    C#API接口调试工具
    微信小程序之自定义组件与使用
    微信小程序开发之拼接json数组字符串
    IdentityServer4 中文文档 -11- (快速入门)添加基于 OpenID Connect 的用户认证
    IdentityServer4 中文文档 -10- (快速入门)使用密码保护API
  • 原文地址:https://www.cnblogs.com/ccaa/p/12628282.html
Copyright © 2011-2022 走看看