用信号量(Semaphore)解决过山车问题(The Roller Coaster Problem)

本文介绍了如何使用C#的SemaphoreSlim类来解决过山车问题,涉及多线程同步和并发控制。作者通过实例代码详细展示了如何模拟乘客上下车的过程,并确保线程安全。参考了相关书籍和微软官方文档,以及不同资源中的解决方案,最终成功实现了一个完整的程序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上周五,一个同学问我一个问题:The Roller Coaster Problem,如何编程实现。

我理解了问题本身的内涵,但要求用C#的信号量SemaphoreSlim的API搞定,我当时有点懵了。曾经学操作系统的时候,一定是睡过去了……

而且在工作时,大部分时间做前端开发,很少关注多线程处理高并发的场景。

周六要加班,没办法,只能等到周日,花了点时间研究,终于写出来可运行的代码。

 

参考资料:

微软的官方文档:

https://docs.microsoft.com/en-us/dotnet/api/system.threading.semaphoreslim?view=net-5.0

有一本不错的书叫 《The Little Book of Semaphores》,是一本免费的书,作者是Allen B. Downey。里面有一个章节是讲过山车问题的,可以在这里下载:

http://greenteapress.com/semaphores/LittleBookOfSemaphores.pdf

这里有中文翻译:

https://blog.csdn.net/booksyhay/article/details/82897462

 

在密歇根理工大学(MTU)的网站上,也有一篇文章讲过山车问题的,是用C++写的代码。

https://pages.mtu.edu/~shene/NSF-3/e-Book/SEMA/TM-example-roller.html

 

实现代码:

实际上,那个同学说的问题,跟网上能找到的资料还有点差别,看来老师还是精心准备过的。

我在实现时,也考虑到了一些复杂情况,尽量都能模拟到。

 

using System;
using System.Threading;

namespace TestSem
{
    class Program
    {
        public static int rollerCoasterCapacity = 4;
        public static int passengersInLine = 9; // 共9个人,最后一轮,只有一人上车
        public static int noOfRides = (int)Math.Ceiling((decimal)passengersInLine / (decimal)rollerCoasterCapacity); // 总共跑几圈
        public static int currentRide = 0; // 当前的圈数
        public static int passengersInCar = 0; // 在车上的乘客数量

        // 乘客登入结束。如果某一轮的乘客满足应乘人数,则立即发车
        private static SemaphoreSlim passengersBoadingOver = new SemaphoreSlim(0, rollerCoasterCapacity);
        // 该上的乘客,全都上车,达到应乘人数
        private static SemaphoreSlim allAboard = new SemaphoreSlim(0);
        // 开车
        private static SemaphoreSlim ridingSem = new SemaphoreSlim(0);
        // 开始下车
        private static SemaphoreSlim startExitSem = new SemaphoreSlim(0, rollerCoasterCapacity);
        // 全部下车
        private static SemaphoreSlim allExited = new SemaphoreSlim(0, rollerCoasterCapacity);

        static void Main(string[] args)
        {
            // 过山车有自己的一个线程
            Thread thrd = new Thread(new ThreadStart(RollerCoster));
            thrd.Start();

            // 给每个乘客创建其专有的线程
            for (int i = 0; i < passengersInLine; i++)
            {
                Thread passengerThread = new Thread(new ThreadStart(Passenger));
                passengerThread.Name = "Passenger_" + i;
                Thread.Sleep(500);
                passengerThread.Start();
            }
        }
        
        private static void Passenger()
        {
            string passengerName = Thread.CurrentThread.Name;
            Console.WriteLine("乘客 {0} 开始等车", passengerName);
            passengersBoadingOver.Wait(); // 等待上车的指示
            Console.WriteLine("{0}开始上车", passengerName);
            Thread.Sleep(500);

            Console.WriteLine("{0}已经上车", passengerName);
            Interlocked.Increment(ref passengersInCar);
            //passengersInCar++; // 注意,多线程环境下,不要这样做。这样会使结果不准确

            Console.WriteLine("当前车上的人数: " + passengersInCar);
            int passengersCount = passengersCountOfTheRide();
            if (passengersBoadingOver.CurrentCount == 0 &&
                passengersInCar == passengersCount)
            {
                allAboard.Release();
                Console.WriteLine("达到应乘人数{0},可开车了", passengersCount);
            }
            // 坐在车上等着开动,以及到站后下车。这个过程中,不需要做任何事情
            ridingSem.Wait();

            // 阶段3: 等车到站
            Console.WriteLine("乘客{0}得到通知车开了,不能下车", passengerName);
            startExitSem.Wait();
            Console.WriteLine("乘客{0}得到停车信号,开始下车", passengerName);
            Interlocked.Decrement(ref passengersInCar);

            // 阶段4,通知车子,已经下完
            Console.WriteLine(Thread.CurrentThread.Name + ". After get off passengersInCar " + passengersInCar);
            if (passengersInCar.Equals(0)) 
            {
                Console.WriteLine("车上的人都下完了###");
                allExited.Release();
            }
            // 下车是每个人独自进行的,全部下完后,才能继续上人。
                
        }

        private static int passengersCountOfTheRide()
        {
            int result = rollerCoasterCapacity;
            if (currentRide == noOfRides)
            {
                result = rollerCoasterCapacity % currentRide;
            }
            return result;
        }

        private static void RollerCoster()
        {
            Console.WriteLine("RollerCoster程序开始执行,共需要跑{0}圈", noOfRides);
            while (true)
            {
                //车辆停好,通知乘客上车
                if (currentRide == noOfRides)
                {
                    Console.WriteLine("所有rides跑完,结束");
                    return;
                }
                currentRide++;
                // 如果是最后一圈,则要计算一下最后一圈可以上的人数是多少。
                int passengersCount = passengersCountOfTheRide();
                Console.WriteLine("跑第{0}圈,应乘人数:{1}", currentRide, passengersCount);
                passengersBoadingOver.Release(passengersCount);
                passengersInCar = 0;
                Console.WriteLine("All Entered");
                Thread.Sleep(1500);


                // 阶段2,等待乘客上车
                allAboard.Wait();
                Console.WriteLine("All Boarded");
                Thread.Sleep(1500);

                // 阶段3,车子行驶阶段
                ridingSem.Release(rollerCoasterCapacity); // 某一轮行驶开始
                Console.WriteLine("Ride Begins");
                Thread.Sleep(1500);


                // 阶段4,下车阶段
                startExitSem.Release(rollerCoasterCapacity); // 通知所有乘客下车
                allExited.Wait();
                Console.WriteLine("乘客已下完,某一轮ride结束");
            }
        }

    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值