哲学家就餐问题

最近在看操作系统,研究并发处理方面的问题,试着用C#写了“哲学家就餐”问题,已测试。


using System;
using System.Diagnostics;
using System.Threading;


namespace Philosopher
{
    class Program
    {
        static void Main(string[] args)
        {
            Method(5);
        }


        /// <summary>
        /// 哲学家就餐问题,5个哲学家围坐一张餐桌,每两个科学家之间各摆放一把叉子(或一根筷子),
        /// 科学家只有拿到左右两把叉子才能进餐,科学家只有两种状态:进餐和思考,设计算法让科学家尽快进餐。
        /// </summary>
        /// <param name="n"></param>
        static void Method(int n)
        {
            if (n < 2)
            {
                throw new ArgumentOutOfRangeException("n", "n should be larger than 1.");
            }
            Person[] people = new Person[n];
            bool[] resource = new bool[n]; // resource is the fork or chopstick, false:vacant, true:occupied
            for (int i = 0; i < n; i++)
            {
                people[i] = new Person(i);
                resource[i] = false;
            }


            int hungryCount = n;
            Stopwatch watch = new Stopwatch();
            watch.Start();
            foreach (var p in people)
            {
                Thread thread = new Thread(() =>
                {
                    Console.WriteLine("ThreadId:" + Thread.CurrentThread.ManagedThreadId);
                    while (hungryCount != 0)
                    {
                        if (p.State == 0)
                        {
                            bool canEat = false;
                            lock (resource)
                            {
                                // CanEat() and Eat() must be locked together in the same lock block
                                canEat = CanEat(resource, p.Index);
                                if (canEat)
                                {
                                    Eat(resource, p);
                                    Console.WriteLine("Eating...");
                                }
                            }
                            if (canEat)
                            {
                                Thread.Sleep(5000); // the time the eating progress takes
                                Think(resource, p);
                                hungryCount--;
                                Console.WriteLine("Elapsed:" + watch.Elapsed.TotalMilliseconds);
                            }
                        }
                    }
                });
                thread.Start();
            }
        }


        static bool CanEat(bool[] resource, int i)
        {
            bool result = false;
            //lock (resource)
            //{
            if (!resource[i])
            {
                if (i == 0)
                {
                    result = !resource[resource.Length - 1];
                }
                else
                {
                    result = !resource[i - 1];
                }
                if (result)
                {
                    Console.WriteLine("[CanEat]The {0}th person: both of the left and right fork are vacant.", i + 1);
                }
                else
                {
                    //Console.WriteLine("[CannotEat]The {0}th person: the left fork is vacant but the right one is occupied.", i + 1);
                }
            }
            else
            {
                //Console.WriteLine("[CannotEat]The {0}th person: the left fork is occupied.", i + 1);
            }
            //}
            return result;
        }


        static void Eat(bool[] resource, Person p)
        {
            //lock (resource)
            //{
            resource[p.Index] = true;
            if (p.Index == 0)
            {
                resource[resource.Length - 1] = true;
            }
            else
            {
                resource[p.Index - 1] = true;
            }
            //}
            p.State = 1;
            Console.WriteLine("The {0}th person is eating.", p.Index + 1);
        }


        static void Think(bool[] resource, Person p)
        {
            lock (resource)
            {
                resource[p.Index] = false;


                if (p.Index == 0)
                {
                    resource[resource.Length - 1] = false;
                }
                else
                {
                    resource[p.Index - 1] = false;
                }
            }
            p.State = 2;
            Console.WriteLine("The {0}th person just has finished the meal.", p.Index + 1);
        }
    }


    public class Person
    {
        /// <summary>
        ///  the index of person, for printing log
        /// </summary>
        public int Index { get; set; }
        
        /// <summary>
        /// the state of person, 0:hungry, 1:eating, 2:thinking
        /// </summary>
        public int State { get; set; } 
        public Person(int index)
            : this(index, 0)
        {
        }


        public Person(int index, int state)
        {
            this.Index = index;
            this.State = 0;
        }
    }
}

后来仔细想想,改进了一下程序

1. 把CanEat()与Eat()合并成CanEat(),这样lock block写在方法里,更合理,外层调用也更简洁

2. 希望各个子线程几乎完全同时进行,把初始化线程代码与线程启动分离成不同循环(上面代码运行时有可能有些线程还没初始化完,有的初始化完的已经开始跑了,可能造成最终运行时间比预想的多)

3. 并且希望main thread可以等所有子线程完成(异步)再去做些其它事情(上面的代码main可以做其它事但是是与子线程同时进行的,即同步),可以使用AutoResetEvent,改进代码:

using System;
using System.Diagnostics;
using System.Threading;


namespace Philosopher
{
    class Program
    {
        static void Main(string[] args)
        {
            Method(5);
        }


        /// <summary>
        /// 哲学家就餐问题,5个哲学家围坐一张餐桌,每两个科学家之间各摆放一把叉子(或一根筷子),
        /// 科学家只有拿到左右两把叉子才能进餐,科学家只有两种状态:进餐和思考,设计算法让科学家尽快进餐。
        /// </summary>
        /// <param name="n"></param>
        static void Method(int n)
        {
            if (n < 2)
            {
                throw new ArgumentOutOfRangeException("n", "n should be larger than 1.");
            }
            Person[] people = new Person[n];
            bool[] resource = new bool[n]; // resource is the fork or chopstick, false:vacant, true:occupied
            for (int i = 0; i < n; i++)
            {
                people[i] = new Person(i);
                resource[i] = false;
            }

            int hungryCount = n;
            Stopwatch watch = new Stopwatch();
            watch.Start();
            Thread[] threads = new Thread[n];
            AutoResetEvent[] events = new AutoResetEvent[n];
            foreach (var p in people)
            {
                events[p.Index] = new AutoResetEvent(false);
                threads[p.Index] = new Thread(() =>
                {
                    Console.WriteLine("ThreadId:" + Thread.CurrentThread.ManagedThreadId);
                    while (hungryCount != 0)
                    {
                        if (p.State == 0 && CanEat(resource, p))
                        {
                            Thread.Sleep(5000); // the time the eating progress takes
                            Think(resource, p);
                            hungryCount--;
                            //Console.WriteLine("Elapsed:" + watch.Elapsed.TotalMilliseconds);
                        }
                    }
                    Console.WriteLine("[END]ThreadId:" + Thread.CurrentThread.ManagedThreadId);
                    events[p.Index].Set();
                });
            }

            for (int i = 0; i < n; i++)
            {
                threads[i].Start();
            }
            AutoResetEvent.WaitAll(events);
            Console.WriteLine("total elapsed:" + watch.Elapsed.TotalMilliseconds);
            watch.Stop();
        }


        static bool CanEat(bool[] resource, Person p)
        {
            bool result = false;
            lock (resource)
            {
                if (!resource[p.Index])
                {
                    if (p.Index == 0)
                    {
                        result = !resource[resource.Length - 1];
                    }
                    else
                    {
                        result = !resource[p.Index - 1];
                    }
                    if (result)
                    {
                        Console.WriteLine("[CanEat]The {0}th person: both of the left and right fork are vacant.", p.Index + 1);
                        resource[p.Index] = true;
                        if (p.Index == 0)
                        {
                            resource[resource.Length - 1] = true;
                        }
                        else
                        {
                            resource[p.Index - 1] = true;
                        }
                        p.State = 1;
                        Console.WriteLine("The {0}th person is eating.", p.Index + 1);
                        Console.WriteLine("Eating...");
                    }
                    else
                    {
                        //Console.WriteLine("[CannotEat]The {0}th person: the left fork is vacant but the right one is occupied.", i + 1);
                    }
                }
                else
                {
                    //Console.WriteLine("[CannotEat]The {0}th person: the left fork is occupied.", i + 1);
                }
            }
            return result;
        }

        static void Think(bool[] resource, Person p)
        {
            lock (resource)
            {
                resource[p.Index] = false;


                if (p.Index == 0)
                {
                    resource[resource.Length - 1] = false;
                }
                else
                {
                    resource[p.Index - 1] = false;
                }
            }
            p.State = 2;
            Console.WriteLine("The {0}th person just has finished the meal.", p.Index + 1);
        }
    }


    public class Person
    {
        /// <summary>
        ///  the index of person, for printing log
        /// </summary>
        public int Index { get; set; }

        /// <summary>
        /// the state of person, 0:hungry, 1:eating, 2:thinking
        /// </summary>
        public int State { get; set; }

        public Person(int index)
            : this(index, 0)
        {
        }

        public Person(int index, int state)
        {
            this.Index = index;
            this.State = 0;
        }
    }
}


上面第三点使main thread异步的方法,除了使用AutoResetEvent,还可以调用Join(),这个更常规,但是没有AutoResetEvent使用灵活(比如你不想等线程完全结束再异步,AutoResetEvent可以实现想在线程的哪一步开始结束异步都可以,就是说可以进行一半就结束异步,该线程后面部分与其它线程同步进行,只需调用Set()即可)。注意这里有多个子线程要等待,所以要在启动子线程的循环后再另写一个循环去Join,不可以只写一个循环Start之后下一句直接Join。正确写法如:

            for (int i = 0; i < n; i++)
            {
                threads[i].Start();
            }
            for (int i = 0; i < n; i++)
            {
                threads[i].Join();
            }


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值