zoukankan      html  css  js  c++  java
  • C#多线程编程实战(二)

    1.1 简介

    为了防止一个应用程序控制CPU而导致其他应用程序和操作系统本身永远被挂起这一可能情况,操作系统不得不使用某种方式将物理计算分割为一些虚拟的进程,并给予每个执行程序一定量的计算能力。此外操作系统必须始终能够优先访问CPU,并能调整不同程序访问CPU的优先级。线程正式这一慨念的实现。

    多线程优点:可以同时执行多个计算任务,有可能提高计算机的处理能力,使得计算机每秒能执行越来越多的命令

    多线程缺点:消耗大量的操作系统资源。多个线程共享一个处理器将导致操作系统忙于管理这些线程,而无法运行程序。

    1.2 创建线程

    using System;
    using System.Threading;
    
    namespace MulityThreadNote
    {
        class Program
        {
            static void Main(string[] args)
            {
                
                Thread t1 = new Thread(new ThreadStart(PrintNumbers));//无参数的委托
                t1.Start();
                
                Thread t2 = new Thread(new ParameterizedThreadStart(PrintNumbers));//有参数的委托
                t2.Start(10);
                Console.ReadLine();
            }
    
            static void PrintNumbers()
            {
                Console.WriteLine("Starting...");
                for (int i = 0; i < 10; i++)
                {
                    Console.WriteLine(i);
                }
            }
    
            //注意:要使用ParameterizedThreadStart,定义的参数必须为object
            static void PrintNumbers(object count)
            {
                Console.WriteLine("Starting...");
                for (int i = 0; i < Convert.ToInt32(count); i++)
                {
                    Console.WriteLine(i);
                }
            }
        }
    }

    注释:我们只需指定在不同线程运行的方法名,而C#编译器会在后台创建这些对象

    1.3 暂停线程

    using System;
    using System.Threading;
    
    namespace MulityThreadNote
    {
        class Program
        {
            static void Main(string[] args)
            {
                
                Thread t1 = new Thread(PrintNumbersWithDelay);
                t1.Start();
                PrintNumbers();
                Console.ReadLine();
            }
    
            static void PrintNumbers()
            {
                Console.WriteLine("Starting...");
                for (int i = 0; i < 10; i++)
                {
                    
                    Console.WriteLine(i);
                }
            }
    
            static void PrintNumbersWithDelay()
            {
                Console.WriteLine("Starting...");
                for (int i = 0; i < 10; i++)
                {
                    Thread.Sleep(TimeSpan.FromSeconds(2));
                    Console.WriteLine(i);
                }
            }
        }
    }

    注释:使用Thread.Sleep(TimeSpan.FromSeconds(2));暂停线程

    1.4 线程等待

    using System;
    using System.Threading;
    
    namespace MulityThreadNote
    {
        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine("Starting...");
                Thread t = new Thread(PrintNumbersWithDelay);
                t.Start();
                t.Join();   //使用Join等待t完成
                PrintNumbers();
                Console.WriteLine("THread Complete");
                Console.ReadLine();
            }
    
            static void PrintNumbers()
            {
                Console.WriteLine("Starting...");
                for (int i = 0; i < 10; i++)
                {
                    
                    Console.WriteLine(i);
                }
            }
    
            static void PrintNumbersWithDelay()
            {
                Console.WriteLine("Starting...");
                for (int i = 0; i < 10; i++)
                {
                    Thread.Sleep(TimeSpan.FromSeconds(2));
                    Console.WriteLine(i);
                }
            }
        }
    }

    注释:使用t.Join();   等待t完成

    1.5 终止线程

    using System;
    using System.Threading;
    
    namespace MulityThreadNote
    {
        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine("Starting Program...");
                Thread t1 = new Thread(PrintNumbersWithDelay);
                t1.Start();
                Thread.Sleep(TimeSpan.FromSeconds(6));
                t1.Abort();    //使用Abort()终止线程
                Console.WriteLine("Thread t1 has been aborted");
                Thread t2 = new Thread(PrintNumbers);
                PrintNumbers();
                Console.ReadLine();
            }
    
            static void PrintNumbers()
            {
                Console.WriteLine("Starting...");
                for (int i = 0; i < 10; i++)
                {
                    
                    Console.WriteLine(i);
                }
            }
    
            static void PrintNumbersWithDelay()
            {
                Console.WriteLine("Starting...");
                for (int i = 0; i < 10; i++)
                {
                    Thread.Sleep(TimeSpan.FromSeconds(2));
                    Console.WriteLine(i);
                }
            }
        }
    }

    注释:使用Thread实例的Abort方法终止线程

    1.6 检测线程状态

    using System;
    using System.Threading;
    
    namespace MulityThreadNote
    {
        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine("Start Program...");
                Thread t1 = new Thread(PrintNumbersWithStatus);
                Thread t2 = new Thread(DoNothing);
                Console.WriteLine(t1.ThreadState.ToString());//获取实例线程状态
                t2.Start();
                t1.Start();
                for (int i = 0; i < 30; i++)
                {
                    Console.WriteLine(t1.ThreadState.ToString());
                }
                Thread.Sleep(TimeSpan.FromSeconds(6));
                t1.Abort();
                Console.WriteLine("thread t1 has been aborted");
                Console.WriteLine(t1.ThreadState.ToString());
                Console.WriteLine(t2.ThreadState.ToString());
                Console.ReadLine();
            }
    
            private static void PrintNumbersWithStatus()
            {
                Console.WriteLine("Starting...");
                Console.WriteLine(Thread.CurrentThread.ThreadState.ToString());//获取当前线程状态
                for (int i = 0; i < 10; i++)
                {
                    Thread.Sleep(TimeSpan.FromSeconds(2));
                    Console.WriteLine(i);
                }
            }
    
            private static void DoNothing()
            {
                Thread.Sleep(TimeSpan.FromSeconds(2));
            }
        }
    }

    注释:使用Thread.ThreadState获取线程的运行状态。ThreadState是一个C#枚举。谨记:不要在程序中使用线程终止,否则可能会出现意想不到的结果

    1.7 线程优先级

    using System;
    using System.Diagnostics;
    using System.Threading;
    
    namespace MulityThreadNote
    {
        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine($"Current thread priority: {Thread.CurrentThread.Priority}");
                Console.WriteLine("Running on all cores available");//获取实例线程状态
                RunThreads();
    
                Thread.Sleep(TimeSpan.FromSeconds(2));
                Console.WriteLine("Running on a single Core");
                //让操作系统的所有线程运行在单个CPU核心上
                Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1);
                RunThreads();
                Console.ReadLine();
            }
    
            private static void RunThreads()
            {
                var sample = new ThreadSample();
    
                var t1 = new Thread(sample.CountNumbers);
                t1.Name = "Thread One";
                var t2 = new Thread(sample.CountNumbers);
                t2.Name = "Thread Two";
    
                t1.Priority = ThreadPriority.Highest;//使用Priority设置线程的优先级
                t2.Priority = ThreadPriority.Lowest;
                t1.Start();
                t2.Start();
    
                Thread.Sleep(TimeSpan.FromSeconds(2));
                sample.Stop();
            }
        }
    
        class ThreadSample
        {
            private bool _isStopped = false;
            public void Stop()
            {
                _isStopped = true;
            }
    
            public void CountNumbers()
            {
                long counter = 0;
                while (!_isStopped)
                {
                    counter++;
                }
                Console.WriteLine($"{Thread.CurrentThread.Name} with {Thread.CurrentThread.Priority} priority has a count={counter.ToString("N0")}");
            }
        }
    }

    注释:单核执行多线程耗费的时间比多核的多很多

    1.8 前台线程和后台线程

    using System;
    using System.Diagnostics;
    using System.Threading;
    
    namespace MulityThreadNote
    {
        class Program
        {
            static void Main(string[] args)
            {
                var sampleForground = new ThreadSample(10);
                var sampleBackground = new ThreadSample(20);
                var t1 = new Thread(sampleForground.CountNumbers);
                t1.Name = "ForegroundThread";   //没有明确声明的均为前台线程
                var t2 = new Thread(sampleBackground.CountNumbers);
                t2.Name = "BackgroundThread";
                t2.IsBackground = true;    //设置为后台线程
    
                t1.Start();
                t2.Start();
            }
        }
    
        class ThreadSample
        {
            private readonly int _iteration;
    
            public ThreadSample(int iteration)
            {
                _iteration = iteration;
            }
    
            public void CountNumbers()
            {
                for (int i = 0; i < _iteration; i++)
                {
                    Thread.Sleep(TimeSpan.FromSeconds(0.5));
                    Console.WriteLine($"{Thread.CurrentThread.Name} prints {i}");
                }
            }
        }
    }

    注释:进程会等待所有的前台线程完成后再结束工作,但是如果只剩下后台线程,则会直接结束工作

    1.9 向线程传递参数

    using System;
    using System.Diagnostics;
    using System.Threading;
    
    namespace MulityThreadNote
    {
        class Program
        {
            static void Main(string[] args)
            {
                ThreadSample sample = new ThreadSample(5);
    
                Thread t1 = new Thread(sample.CountNumbers);
                t1.Name = "ThreadOne";
                t1.Start();
                t1.Join();
                Console.WriteLine("--------------------------");
    
                Thread t2 = new Thread(Count);
                t2.Name = "ThreadTwo";
                t2.Start(3);
                t2.Join();
                Console.WriteLine("--------------------------");
    
                //使用lambda表达式引用另一个C#对方的方式被称为闭包。当在lambda表达式中使用任何局部变量时,C#会生成一个类,并将该变量作为该类的一个属性,但是我们无须定义该类,C#编译器会自动帮我们实现
                Thread t3 = new Thread(()=> CountNumbers(5));
                t3.Name = "ThreadThree";
                t3.Start();
                t3.Join();
                Console.WriteLine("--------------------------");
    
                int i = 10;
                Thread t4 = new Thread(() => PrintNumber(i));
                
                i = 20;
                Thread t5 = new Thread(() => PrintNumber(i));
                t4.Start();
                t5.Start();
                //t4, t5都会输出20, 因为t4,t5没有Start之前i已经变成20了
                Console.ReadKey();
            }
    
            static void Count(object iterations)
            {
                CountNumbers((int)iterations);
            }
    
            static void CountNumbers(int iterations)
            {
                for (int i = 1; i <= iterations; i++)
                {
                    Thread.Sleep(TimeSpan.FromSeconds(0.5));
                    Console.WriteLine($"{Thread.CurrentThread.Name} prints {i}");
                }
            }
    
            static void PrintNumber(int number)
            {
                Console.WriteLine(number);
            }
        }
    
        class ThreadSample
        {
            private readonly int _iteration;
    
            public ThreadSample(int iteration)
            {
                _iteration = iteration;
            }
    
            public void CountNumbers()
            {
                for (int i = 1; i <= _iteration; i++)
                {
                    Thread.Sleep(TimeSpan.FromSeconds(0.5));
                    Console.WriteLine($"{Thread.CurrentThread.Name} prints {i}");
                }
            }
        }
    }

    注释:也可以使用ThreadStart传递参数

    1.10 使用C# lock关键字

    using System;
    using System.Diagnostics;
    using System.Threading;
    
    namespace MulityThreadNote
    {
        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine("Incorrect Counter");
                Counter c1 = new Counter();
                var t1 = new Thread(() => TestCounter(c1));
                var t2 = new Thread(() => TestCounter(c1));
                var t3 = new Thread(() => TestCounter(c1));
                t1.Start();
                t2.Start();
                t3.Start();
                t1.Join();
                t2.Join();
                t3.Join();
                Console.WriteLine($"Total Count: {c1.Count}");
                Console.WriteLine("------------------------");
    
                Console.WriteLine("Correct counter");
                CounterWithLock c2 = new CounterWithLock();
                t1 = new Thread(() => TestCounter(c2));
                t2 = new Thread(() => TestCounter(c2));
                t3 = new Thread(() => TestCounter(c2));
                t1.Start();
                t2.Start();
                t3.Start();
                t1.Join();
                t2.Join();
                t3.Join();
                Console.WriteLine($"Total count:{c2.Count}");
                Console.ReadLine();
            }
    
            static void TestCounter(CounterBase c)
            {
                for (int i = 0; i < 100000; i++)
                {
                    c.Increment();
                    c.Decrement();
                }
            }
    
            class Counter : CounterBase
            {
                public int Count { get; private set; }
                public override void Decrement()
                {
                    Count--;
                }
    
                public override void Increment()
                {
                    Count++;
                }
            }
    
            class CounterWithLock : CounterBase
            {
                private readonly object _asyncRoot = new object();
                public int Count { get; private set; }
                public override void Decrement()
                {
                    lock (_asyncRoot)
                    {
                        Count--;
                    }
                }
    
                public override void Increment()
                {
                    lock (_asyncRoot)
                    {
                        Count++;
                    }
                }
            }
    
            abstract class CounterBase
            {
                public abstract void Increment();
    
                public abstract void Decrement();
            }
        }
    
        class ThreadSample
        {
            private readonly int _iteration;
    
            public ThreadSample(int iteration)
            {
                _iteration = iteration;
            }
    
            public void CountNumbers()
            {
                for (int i = 1; i <= _iteration; i++)
                {
                    Thread.Sleep(TimeSpan.FromSeconds(0.5));
                    Console.WriteLine($"{Thread.CurrentThread.Name} prints {i}");
                }
            }
        }
    }

    注释:不加锁,得出的结果不确定,竞争条件下很容易出错。加锁得出的结果是正确的,但是性能受到了影响

    1.11 使用Monitor类锁定资源

    using System;
    using System.Diagnostics;
    using System.Threading;
    
    namespace MulityThreadNote
    {
        class Program
        {
            static void Main(string[] args)
            {
                object lock1 = new object();
                object lock2 = new object();
                new Thread(() => LockTooMuch(lock1, lock2)).Start();
                lock (lock2)
                {
                    Thread.Sleep(1000);
                    Console.WriteLine("Monitor.TryEnter allows not to get stuck, returning false after a specified timeout is elapsed");
                    //直接使用Monitor.TryEnter, 如果在第二个参数之前还未获取到lock保护的资源会返回false
                    if (Monitor.TryEnter(lock1, TimeSpan.FromSeconds(5)))
                    {
                        Console.WriteLine("Acquired a protected resource successfully");
                    }
                    else
                    {
                        Console.WriteLine("Timeout acquiring a resource");
                    }
                }
                new Thread(() => LockTooMuch(lock1, lock2)).Start();
                Console.WriteLine("-----------------------------");
                /* 下面代码会造成死锁, 所以注释掉
                lock (lock2)
                {
                    Console.WriteLine("This will be a deadlock!");
                    Thread.Sleep(1000);
                    lock (lock1)
                    {
                        Console.WriteLine("Acquired a protected resource successfully");
                    }
                }
                */
            }
    
            static void LockTooMuch(object lock1, object lock2)
            {
                lock (lock1)
                {
                    Thread.Sleep(1000);
                    lock (lock2);
                }
            }
        }
    }

    注释:Monitor.TryEnter在指定的时间内尝试获取指定对象上的排他锁

    1.12 处理异常

    using System;
    using System.Diagnostics;
    using System.Threading;
    
    namespace MulityThreadNote
    {
        class Program
        {
            static void Main(string[] args)
            {
                Thread t = new Thread(FaultyThread);
                t.Start();
                t.Join();
                try
                {
                    t = new Thread(BadFaultyThread);
                    t.Start();
                }
                catch (Exception ex)
                {
                    Console.WriteLine("We won't get here");
                }
            }
            static void BadFaultyThread()
            {
                Console.WriteLine("Starting a faulty thread.....");
                Thread.Sleep(TimeSpan.FromSeconds(2));
                //这个异常主线程无法捕捉到,因为是在子线程抛出的异常。需要在子线程中加入try...catch捕获异常
                throw new Exception("Boom!");
            }
            static void FaultyThread()
            {
                try
                {
                    Console.WriteLine("Starting a faulty thread...");
                    Thread.Sleep(TimeSpan.FromSeconds(1));
                    throw new Exception("Boom");
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Exception handled: {ex.Message}");
                }
            }
        }
    }

    C#多线程编程实战 (线程同步)

    2.1 简介

    竞争条件:多个线程同时使用共享对象。需要同步这些线程使得共享对象的操作能够以正确的顺序执行

    线程同步问题:多线程的执行并没有正确的同步,当一个线程执行递增和递减操作时,其他线程需要依次等待

    线程同步解决方案:

    无须共享对象:大部分时候可以通过重新设计来移除共享对象,去掉复杂的同步构造,避免多线程使用单一对象

    必须共享对象:只使用原子操作,一个操作只占用一个量子的时间,无须实现其他线程等待当前操作完成

    内核模式:将等待的线程置于阻塞状态,消耗少量的CPU资源,但会引入至少一次上下文切换,适用于线程等待较长时间

    用户模式:只是简单的等待,线程等待会浪费CPU时间但是可以节省上下文切换消耗的CPU时间,适用于线程等待较短时间

    混合模式:先尝试用户模式,如果等待时间较长,则会切换到内核模式

    2.2 执行基本的原子操作

    using System;
    using System.Threading;
    
    namespace MulityThreadNote
    {
        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine("Incorrect counter");
                //没有限定,会遇到竞争条件,得出的结果大部分不是正确的
                var c = new Counter();
                var t1 = new Thread(() => TestCounter(c));
                var t2 = new Thread(() => TestCounter(c));
                var t3 = new Thread(() => TestCounter(c));
                t1.Start();
                t2.Start();
                t3.Start();
                t1.Join();
                t2.Join();
                t3.Join();
                Console.WriteLine($"Total count:{c.Count}");
                Console.WriteLine("--------------------------");
                Console.WriteLine("Correct counter");
                //使用Interlocked类提供的原子操作方法,无需锁定任何对象可得出正确结果
                var c1 = new CounterNoLock();
                t1 = new Thread(() => TestCounter(c1));
                t2 = new Thread(() => TestCounter(c1));
                t3 = new Thread(() => TestCounter(c1));
                t1.Start();
                t2.Start();
                t3.Start();
                t1.Join();
                t2.Join();
                t3.Join();
                Console.WriteLine($"Total count:{c1.Count}");
                Console.ReadLine();
            }
            static void TestCounter(CounterBase c)
            {
                for (int i = 0; i < 100000; i++)
                {
                    c.Increment();
                    c.Decrement();
                }
            }
            class Counter : CounterBase
            {
                private int _count;
                public int Count { get { return _count; } }
    
                public override void Decrement()
                {
                    _count--;
                }
    
                public override void Increment()
                {
                    _count++;
                }
            }
    
            class CounterNoLock : CounterBase
            {
                private int _count;
                public int Count { get { return _count; } }
    
                public override void Decrement()
                {
                    //Interlocked提供了Increment()、Decrement()和Add等基本数学操作的原子方法
                    Interlocked.Decrement(ref _count);
                }
    
                public override void Increment()
                {
                    Interlocked.Increment(ref _count);
                }
            }
    
            abstract class CounterBase
            {
                public abstract void Increment();
                public abstract void Decrement();
            }
        }
    }

    注释:Interlocked提供了Increment()、Decrement()和Add等基本数学操作的原子方法,不用锁也可以得出正确结果

    2.3 使用Mutex类

    using System;
    using System.Threading;
    
    namespace MulityThreadNote
    {
        class Program
        {
            static void Main(string[] args)
            {
                const string MutexName = "CSharpThreadingCookbook";
                //Mutex是一种原始的同步方式,只对一个线程授予对共享资源的独占访问
                //定义一个指定名称的互斥量,设置initialOwner标志为false
                using (var m = new Mutex(false, MutexName))
                {
                    //如果互斥量已经被创建,获取互斥量,否则就执行else语句
                    if (!m.WaitOne(TimeSpan.FromSeconds(5), false))
                    {
                        Console.WriteLine("Second instance is running!");
                    }
                    else
                    {
                        Console.WriteLine("Running!");
                        Console.ReadLine();
                        m.ReleaseMutex();
                    }
                }
                //如果再运行同样的程序,则会在5秒内尝试获取互斥量,如果第一个程序按下了任何键,第二个程序开始执行。
                //如果等待5秒钟,第二个程序将无法获取该互斥量
            }
        }
    }

    注释:互斥量是全局操作对象,必须正确关闭,最好用using

    2.4 使用SemaphoreSlim类

    using System;
    using System.Threading;
    
    namespace MulityThreadNote
    {
        class Program
        {
            static void Main(string[] args)
             {
                //启动6个线程,启动的顺序不一样
                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();
                }
    
                Console.ReadLine();
            }
            //SemaphoreSlim的构造函数参数为允许的并发线程数目
            static SemaphoreSlim semaphore = new SemaphoreSlim(4);
    
            static void AccessDatabase(string name, int seconds)
            {
                Console.WriteLine($"{name} waits to access a database");
                semaphore.Wait();
                Console.WriteLine($"{name} was granted an access to a database");
                Thread.Sleep(TimeSpan.FromSeconds(seconds));
                Console.WriteLine($"{name} is Completed");
                //调用Release方法说明线程已经完成,可以开启一个新的线程了
                semaphore.Release();
            }
        }
    }

    注释:这里使用了混合模式,允许我们在等待时间很短的情况下无需上下文切换。SemaphoreSlim并不使用Windows内核信号量,而且也不支持进程间同步

    2.5 使用AutoResetEvent类

    using System;
    using System.Threading;
    
    namespace MulityThreadNote
    {
        class Program
        {
            static void Main(string[] args)
             {
                Thread t = new Thread(() => Process(10));
                t.Start();
                Console.WriteLine("Waiting for another thread to complete work");
                //开启一个线程后workEvent等待,直到收到Set信号
                workEvent.WaitOne();
                Console.WriteLine("First operation is complete");
                Console.WriteLine("Performing an operation on a main thread");
                Thread.Sleep(TimeSpan.FromSeconds(5));
                mainEvent.Set();
                Console.WriteLine("Now running the second operation on a second thread");
                workEvent.WaitOne();
                Console.WriteLine("Second operation is complete");
                Console.ReadLine();
            }
            //初始状态为unsignaled,子线程向主线程发信号
            private static AutoResetEvent workEvent = new AutoResetEvent(false);
            //初始状态为unsignaled,主线程向子线程发信号
            private static AutoResetEvent mainEvent = new AutoResetEvent(false);
    
            static void Process(int seconds)
            {
                Console.WriteLine("Starting a long running work...");
                Thread.Sleep(TimeSpan.FromSeconds(seconds));
                Console.WriteLine("Work is done!");
                workEvent.Set();//将事件设为终止状态允许一个或多个线程继续
                Console.WriteLine("Waiting for a main thread to complete its work");
                
                mainEvent.WaitOne();//阻止当前线程,直到mainEvent收到信号
                Console.WriteLine("Starting second operation...");
                Thread.Sleep(TimeSpan.FromSeconds(seconds));
                Console.WriteLine("Work is done");
                workEvent.Set();
            }
        }
    }

    注释:AutoResetEvent采用的是内核模式,所以等待时间不能太长

    2.6 使用ManualResetEventSlim类

    using System;
    using System.Threading;
    
    namespace MulityThreadNote
    {
        class Program
        {
            static void Main(string[] args)
             {
                Thread t1 = new Thread(() => TravelThroughGates("Thread 1", 5));
                Thread t2 = new Thread(() => TravelThroughGates("Thread 2", 6));
                Thread t3 = new Thread(() => TravelThroughGates("Thread 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 second time");
                mainEvent.Set();
                Thread.Sleep(TimeSpan.FromSeconds(2));
                Console.WriteLine("The gates have been closed!");
                mainEvent.Reset();
                Console.ReadLine();
            }
            static ManualResetEventSlim mainEvent = new ManualResetEventSlim(false);
    
            static void TravelThroughGates(string threadName, int seconds)
            {
                Console.WriteLine($"{threadName} falls to sleep");
                Thread.Sleep(TimeSpan.FromSeconds(seconds));
                Console.WriteLine($"{threadName} waits for the gates to open!");
                mainEvent.Wait();//阻止当前线程
                Console.WriteLine($"{threadName} enter the gates!");
            }
        }
    }

    注释:ManualResetEventSlim工作方式像人群通过的大门,一直保持大门敞开直到调用reset,set相当于打开大门,reset相当于关闭大门

    2.7 使用CountDownEvent类

    using System;
    using System.Threading;
    
    namespace MulityThreadNote
    {
        class Program
        {
            static void Main(string[] args)
             {
                Console.WriteLine("Starting two operations");
                Thread t1 = new Thread(() => PerformOperation("Operation 1 is completed", 4));
                Thread t2 = new Thread(() => PerformOperation("Operation 2 is completed", 8));
                t1.Start();
                t2.Start();
                //开启了两个线程,调用Wait方法阻止当前线程,知道所有线程都完成
                countdown.Wait();
                Console.WriteLine("Both operations have been completed");
                countdown.Dispose();
                Console.ReadLine();
            }
            //计数器初始化CountdownEvent实例,计数器表示:当计数器个数完成操作发出信号
            static CountdownEvent countdown = new CountdownEvent(2);
    
            static void PerformOperation(string message, int seconds)
            {
                Thread.Sleep(TimeSpan.FromSeconds(seconds));
                Console.WriteLine(message);
                //向CountdownEvent注册信息,并减少当前计数器数值
                countdown.Signal();
            }
        }
    }

    注释:如果Signal方法没有达到指定的次数,那么countdown.wait()会一直等待,所以请确保所有线程完成后都要调用Signal方法

    2.8 使用Barrier类

    using System;
    using System.Threading;
    
    namespace MulityThreadNote
    {
        class Program
        {
            static void Main(string[] args)
             {
                Thread t1 = new Thread(() => PlayMusic("the gutarist", "play an amazing solo", 5));
                Thread t2 = new Thread(() => PlayMusic("the signer", "sing his song", 2));
                t1.Start();
                t2.Start();
                Console.ReadLine();
            }
            //后面的Lamda表达式是回调函数。执行完SignalAndWait后执行
            static Barrier barrier = new Barrier(2, b=>Console.WriteLine($"End of phase {b.CurrentPhaseNumber + 1}"));
    
            static void PlayMusic(string name, string message, int seconds)
            {
                for (int i = 0; i < 3; i++)
                {
                    Console.WriteLine("===========================");
                    Thread.Sleep(TimeSpan.FromSeconds(seconds));
                    Console.WriteLine($"{name} starts to {message}");
                    Thread.Sleep(TimeSpan.FromSeconds(seconds));
                    Console.WriteLine($"{name} finishes to {message}");
                    //等所有调用线程都结束
                    barrier.SignalAndWait();
                }
            }
        }
    }

    注释:

    2.9 使用ReaderWriterlockSlim类

    using System;
    using System.Collections.Generic;
    using System.Threading;
    
    namespace MulityThreadNote
    {
        class Program
        {
            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(() => Write("Thread 1")) { IsBackground = true }.Start();
                new Thread(() => Write("Thread 2")) { IsBackground = true }.Start();
                Thread.Sleep(TimeSpan.FromSeconds(30));
                Console.ReadLine();
            }
            //实现线程安全
            static ReaderWriterLockSlim _rw = new ReaderWriterLockSlim();
            static Dictionary<int, int> items = new Dictionary<int, int>();
    
            static void Read()
            {
                Console.WriteLine("Reading contents of a dictionary");
                while (true)
                {
                    try
                    {
                        //读锁
                        _rw.EnterReadLock();
                        foreach (var key in items.Keys)
                        {
                            Thread.Sleep(TimeSpan.FromSeconds(0.1));
                        }
                    }
                    finally
                    {
                        //计数为0时退出读取模式
                        _rw.ExitReadLock();
                    }
                }
            }
    
            static void Write(string threadName)
            {
                while (true)
                {
                    try
                    {
                        int newKey = new Random().Next(250);
                        _rw.EnterUpgradeableReadLock();
                        if (!items.ContainsKey(newKey))
                        {
                            try
                            {
                                //写锁
                                _rw.EnterWriteLock();
                                items[newKey] = 1;
                                Console.WriteLine($"New key {newKey} is added to a dictionary by a {threadName}");
                            }
                            finally
                            {
                                //计数为0时退出写入模式
                                _rw.ExitWriteLock();
                            }
                        }
                        Thread.Sleep(TimeSpan.FromSeconds(0.1));
                    }
                    finally
                    {
                        //计数为0时退出可升级模式
                        _rw.ExitUpgradeableReadLock();
                    }
                }
            }
        }
    }

    注释:从集合读取数据时,根据当前数据决定是否获取一个写锁并修改该集合。获取写锁后集合会处于阻塞状态。

    2.10 使用SpinWait类

    using System;
    using System.Collections.Generic;
    using System.Threading;
    
    namespace MulityThreadNote
    {
        class Program
        {
            static void Main(string[] args)
             {
                Thread t1 = new Thread(UserModeWait);
                Thread t2 = new Thread(HybridSpinWait);
                Console.WriteLine("Running user mode waiting");
                t1.Start();
                Thread.Sleep(20);
                _isComplete = true;
                Thread.Sleep(TimeSpan.FromSeconds(1));
                _isComplete = false;
                Console.WriteLine("Running hybrid SpinWait construct waiting");
                t2.Start();
                Thread.Sleep(5);
                _isComplete = true;
                Console.ReadLine();
            }
            //volatile 一个字段可能会被多个线程同时修改,不会被编译器和处理器优化为只能被单个线程访问
            static volatile bool _isComplete = false;
    
            static void UserModeWait()
            {
                while (!_isComplete)
                {
                    Console.WriteLine(".");
                }
                Console.WriteLine();
                Console.WriteLine("Waiting is complete");
            }
    
            static void HybridSpinWait()
            {
                var w = new SpinWait();
                while (!_isComplete)
                {
                    //执行单一自旋
                    w.SpinOnce();
                    //NextSpinWillYield:获取对SpinOnce的下一次调用是否将产生处理,同时触发强制上下文切换
                    //显示线程是否切换为阻塞状态
                    Console.WriteLine(w.NextSpinWillYield);
                }
                Console.WriteLine("Waiting is complete");
            }
        }
    }
  • 相关阅读:
    zoj 3279 线段树 OR 树状数组
    fzu 1962 树状数组 OR 线段树
    hdu 5057 块状链表
    hdu3487 Play with Chain
    bzoj 1588营业额统计(HNOI 2002)
    poj2823 Sliding Window
    poj2828 Buy Tickets
    poj2395 Out of Hay
    poj3667 Hotel
    poj1703 Lost Cows
  • 原文地址:https://www.cnblogs.com/weihengblogs/p/8671290.html
Copyright © 2011-2022 走看看