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");
        }
    }
}

 

posted @ 2018-03-29 17:44  盛开的雨季  阅读(869)  评论(0编辑  收藏  举报