最近遇到一道机试题目:场景:在一个十字路口,有红绿灯,有5辆车正在由南往北通行,行人是由东往西,有10个人在等待绿灯通行;绿灯时间是45秒,红灯时间是30秒,请考虑使用多线程的方式模拟,车辆运行、红绿灯切换以及行人过街道。 解题思路
见到题目的时候脑海里闪过的就是线程Tread.join(),通过插入其他线程来达到车人通行的切换,亦或者使用线程中创建新线程这种套娃的方式来实现,但是我觉得两种方式若在实际生产环境中并不会这样操作,于是想起了线程安全的字典与队列来模拟这个场景。
线程安全字典模拟红绿灯,队列的入队出队模拟人行和车行的等待。
(解题肯定有很多解法,做完记录下来,说不定能起到抛砖引玉的作用~)
线程安全的字典ConcurrentDictionary二话不说先上官网地址:https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.concurrent.concurrentdictionary-2?view=net-5.0
在多线程的情况下使用普通的字典Dictionary,在肯定会导致数据错乱,这个毫无疑问,而且还会遇到一些 迭代时异常,甚至会导致CPU爆高的情况,这个在 《一线码农》 大佬的一篇车联网CPU爆高文章中有分析到,而线程安全字典它能够适应这种场景。
线程安全的队列ConcurrentQueue二话不说先上官网地址:https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.concurrent.concurrentqueue-1?view=net-5.0
官网定义的ConcurrentQueue为线程程安全的先进先出 (FIFO) 集合,由此来模拟车道与人道等待队列。
不说废话上代码 创建场景按照面向对象的思想,首先需要创建出红绿灯,车道、人道及其等待队列
private static ConcurrentDictionary<bool, int> TrafficLightDictionary = new ConcurrentDictionary<bool, int>(); private static bool _traffic = true;//车行道红绿灯 绿灯True,红灯False 对应人行道 红灯True,绿灯False //车道等待队列 private static ConcurrentQueue<Car> carsQueue = new ConcurrentQueue<Car>(); //人道等待队列 private static ConcurrentQueue<Person> personsQueue = new ConcurrentQueue<Person>(); private readonly static int _carRunTime = 45;//车行45秒 private readonly static int _personRunTime = 30;//人行30秒 初始化红绿灯题目中绿灯45秒红灯30秒,没有考虑黄灯的情况,所以用ConcurrentQueue<bool,int>中的bool值作为Key,绿灯True,红灯False,紧接着正好因为车行时,人不能行,人行时车不能行,故可以只使用一个字典就可以满足红绿灯运行条件。
static void TrafficLightRun(object state) { TrafficLightDictionary.TryGetValue(_traffic, out int carRunTimeValue); TrafficLightDictionary.TryGetValue(!_traffic, out int personTimeValue); //Console.WriteLine($"carRunTimeValue:{carRunTimeValue} personTimeValue:{personTimeValue}"); 测试用 //复位 if (carRunTimeValue <= 0 && personTimeValue <= 0) { TrafficLightDictionary.AddOrUpdate(_traffic, _carRunTime, (_traffic, value) => _carRunTime); TrafficLightDictionary.AddOrUpdate(!_traffic, _personRunTime, (_traffic, value) => _personRunTime); } //车行 if (carRunTimeValue > 0 && carRunTimeValue <= _carRunTime) { TrafficLightDictionary.AddOrUpdate(_traffic, carRunTimeValue, (_traffic, value) => --value); Console.ForegroundColor = ConsoleColor.White; Console.WriteLine($"----当前车行道---绿灯-还剩余{--carRunTimeValue}秒-"); } //人行 else if (personTimeValue > 0 && personTimeValue <= _personRunTime) { TrafficLightDictionary.AddOrUpdate(!_traffic, personTimeValue, (_traffic, Othervalue) => --Othervalue); Console.ForegroundColor = ConsoleColor.White; Console.WriteLine($"----当前人行道---绿灯-还剩余{--personTimeValue}秒-"); } }红绿灯运行逻辑写好之后,创建一个定时器(System.Threading.Tasks.Timer)去定时刷新这个字典,从而控制红绿灯的秒数。
Console.WriteLine("红绿灯初始化运行!"); Timer TrafficLightTimer = new Timer(TrafficLightRun); TrafficLightTimer.Change(0, 1000); 初始化车流和人流题目中说有有5辆车,10个人正在通过这个十字路口,参考实际情况中,每一辆车每一个人都是一个新的线程,车和人都在随机的进入这个红绿灯场景,所以使用一个随机数生成随机人与车加入等待队列,形成车流与人流。
static async void InitCarMode() { while (true) { // 车的数量随机 Random random = new Random(); var carCount = random.Next(10, 50); Console.WriteLine($"推入车流量 {carCount}人"); List<Task> taskFactories = new List<Task>(); for (int i = 0; i < carCount; i++) { Car inQueueCar = new Car() { Id = Guid.NewGuid().ToString(), Name = "模拟车辆" + i, MaxParallel = 4 }; Task task = new Task(() => { CarInQueue(inQueueCar); }); taskFactories.Add(task); task.Start(); } await Task.WhenAll(taskFactories.ToArray()); // Console.WriteLine("--入队完成--"); await Task.Delay(10 * 1000); } } static async void createPersonMode() { while (true) { // 人的数量随机 Random random = new Random(); var personCount = random.Next(10, 40); Console.WriteLine($"推入人流量 {personCount}人"); List<Task> taskFactories = new List<Task>(); for (int i = 0; i < personCount; i++) { Person inQueuePerson = new Person() { Id = Guid.NewGuid().ToString(), Name = "模拟行人" + i, MaxParallel = 5 }; Task task = new Task(() => { PersonInQueue(inQueuePerson); }); taskFactories.Add(task); task.Start(); } await Task.WhenAll(taskFactories.ToArray()); // Console.WriteLine("--入队完成--"); await Task.Delay(10 * 1000); } }