C# 多线程编程ReaderWriterLockSlim应用

下面模拟一个场景:一辆车有X个座位,设置有限个数的爱心座椅,车门一次只能进入N个乘客,Y个乘客排队上车(Y>X),先上车的人座位自由选择(包含爱心座椅),后上车的人群中如果需要特殊照顾(老弱幼孕等),需要优先安排到爱心座椅,如果没有空余的爱心座椅,则可以坐普通座位,剩下没有座位的人只能站立了!(场景设计可能不那么恰当,纯粹学习之用)如何用多线程来模拟乘客上车找座位这个场景呢?

下面来实现这个效果:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Diagnostics;

namespace ConsoleApplication1
{
    class Program
    {
        const int personCount = 30; //设置乘客数
        const int siteCount = 25; //设置车座位数
        const int threadCount = 3;// 设置一次进入车门的人数
        static ReaderWriterLockSlim readWriteLock = new ReaderWriterLockSlim();
        static Queue<Passenger> passengerQueue = new Queue<Passenger>();//存储乘客队列
        static List<Seat> seatList = new List<Seat>();//座位集合
        static CountdownEvent countDown;
        static Stopwatch stopWatch = new Stopwatch();
        static void Main(string[] args)
        {
            InitData();//初始化乘客和座位数据
            stopWatch.Start();
            Console.WriteLine("Stopwatch is running:{0}", stopWatch.IsRunning);
            
            countDown  = new CountdownEvent(threadCount);
            for (int i = 1; i < threadCount+1; i++)
            {
                Thread thread1 = new Thread(DispatchPerson);
                thread1.Name = "线程"+thread1.ManagedThreadId;
                thread1.Start();
            }
           
            countDown.Wait();
            Console.WriteLine("乘客分配完毕。。。");
            countDown.Dispose();
            stopWatch.Stop();
            Console.WriteLine("Total running time:{0}", stopWatch.ElapsedMilliseconds);
            Console.Read();
        }

        static void DispatchPerson()
        {
            while (true)
            {
                readWriteLock.EnterUpgradeableReadLock();
                try
                {
                    if (passengerQueue.Count() == 0)
                    {
                        Console.WriteLine("{0} 结束...", Thread.CurrentThread.ManagedThreadId);
                        countDown.Signal();
                        break;
                    }
                    Passenger p = passengerQueue.Dequeue();
                    Random ran = new Random();
                    int sc = seatList.Count();
                    if (sc > 0)
                    {
                        Seat targetSeat= new Seat();
                        int siteIndex = ran.Next(0, sc);
                        if (p.NeedSpecialCare)
                        {
                            // Thread.Sleep(TimeSpan.FromSeconds(1));
                            targetSeat = seatList.Find(s => s.SpecialCareSite);
                            if (targetSeat.SeatNo>0)
                            {
                                Console.WriteLine("{0}:分配结果:{1},座位:{2} ,爱心座椅(^_^)", Thread.CurrentThread.Name, p.PassengerName, targetSeat.SeatNo);
                            }
                            else
                            {
                                targetSeat = seatList[siteIndex];
                                Console.WriteLine("{0}:分配结果:{1},座位:{2} , sorry, 爱心座椅没有了...", Thread.CurrentThread.Name, p.PassengerName, targetSeat.SeatNo);
                            }
                        }
                        else
                        {
                            targetSeat = seatList[siteIndex];
                            Console.WriteLine("{0}:分配结果:{1},座位:{2} ", Thread.CurrentThread.Name, p.PassengerName, targetSeat.SeatNo);
                        }
                        readWriteLock.EnterWriteLock();
                        try
                        {
                            seatList.Remove(targetSeat);
                        }
                        finally
                        {
                            readWriteLock.ExitWriteLock();
                        }
                    }
                    else
                    {
                        Console.WriteLine("{0}:分配乘客_>{1},无座了...", Thread.CurrentThread.Name, p.PassengerName);
                    }                    
                }
                finally
                {
                    readWriteLock.ExitUpgradeableReadLock();
                }
                Thread.Sleep(TimeSpan.FromSeconds(1)); //等待一会,让其他线程有机会参与运算
                
            }
        }
        static void InitData()
        {
            for (var i = 1; i <= personCount; i++) 
            {
                Passenger p=new Passenger();
                p.PassengerName = String.Format("乘客{0}",i);
                if (i % 10 == 0)
                    p.NeedSpecialCare = true;
                passengerQueue.Enqueue(p);
            }

            for (var i = 1; i <= siteCount; i++)
            {
                Seat st=new Seat();
                st.SeatNo = i;
                if (i % 15 == 0)
                    st.SpecialCareSite = true;
                seatList.Add(st);
            }
        }
    }

    struct Passenger
    {
        public string PassengerName { get; set; }
        public bool NeedSpecialCare { get; set; }
    }

    struct Seat
    {
        public int SeatNo { get; set; }
        public bool SpecialCareSite { get; set; }
    }
}

运行效果:

上面是学习ReaderWriterLockSlim 后动手实践的一个小例子:ReaderWriterLockSlim代表了一个管理资源访问的锁,允许多个线程同时读取,以及独占写;上面的例子中对有对seatList的读取和删除,本身是线程不安全的,需要使用同步机制。

https://technet.microsoft.com/zh-CN/library/system.threading.readerwriterlockslim.enterupgradeablereadlock

关于EnterUpgradeableReadLock 方法援引technet.microsoft.com 中的一段描述:

只有一个线程可以在任何给定时间进入可升级模式。 如果某个线程处于可升级模式,并且有没有线程在等待进入写入模式,任意数量的其他线程可以输入读取的模式,即使有线程在等待进入可升级模式。

如果一个或多个线程在等待进入写入模式,线程的调用 EnterUpgradeableReadLock 方法受到阻止,直到这些线程超时或已进入写入模式,然后又从中退出。

分析本人写的这个例子:每个线程在循环后都是要等待进入写入模式,于是调用EnterUpgradeabelReadLock受阻,变相同步了passengerQueue. 如果试图注释掉EnterUpgradeabelReadLock / ExitUpgradeableReadLock 会报错:类似索引超界!

对多线程的学习,目前只是根据测试反馈结果和一些文档上的描述来揣测同步行为,对线程同步背后计算机的逻辑还不是特别清晰,以上如有错误,欢迎指出!

猜你喜欢

转载自blog.csdn.net/elie_yang/article/details/81457978