c# 多线程学习笔记(五)读者写者问题

问题描述

读者写者也是一个非常著名的同步问题,也叫共享独占锁。问题描述非常简单,有一个写者很多读者,多个读者可以同时读文件,但写者在写文件时不允许有读者在读文件,同样有读者在读文件时写者也不去能写文件。因此要求:①允许多个读者可以同时对文件执行读操作;②同时只允许一个写者往文件中写信息;③任一写者在完成写操作之前不允许其他读者或写者工作;④写者执行写操作前,应让已有的读者和写者全部退出。

问题分析

写者和读者不仅是互斥而且是同步的,为此可以用一个事件或者信号量来实现,

 

这样实现了写者与读者对资源的同步访问,但是这个情况只允许一个写者或者一个读者使用资源,对于写者来说,这是没有问题的,但是无法实现多个读者并发访问资源

 

 只有一个reader能使用资源。

这说明以上解决方案还有问题,那么实际上应该是如果一旦有读者开始读文件,那么写者就应该等待,而此时更多的读者也可以同时读取;而当最后一个读者离开时,此时写者就可以写文件。根据以上分析,我们可以使用一个计数器来判断当前是否有读者读文件。

 

对于计数器,因为并发多个读者访问,为保证正确结果,需要再需要一个信号量来同步计数过程,如下:

 

 根据这个可以写出代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using ThreadUtilities;
namespace ReaderWriter
{
    class Program
    {
        
        static void Main(string[] args)
        {
            ReaderWriterProblem rwp = new ReaderWriterProblem(3);
            rwp.Start();
            rwp.Stop(300);

            //ReaderWriterProblemEx rwp = new ReaderWriterProblemEx(3);
            //rwp.Start();
            //rwp.Stop(300);
        }

        /// <summary>
        /// two thread, one is writer thread,the other is reader thread,
        /// because the reader can't read when the writer is writing,the writer shouldn't write until there is not reader:
        /// SO:
        /// The writer thread is synchronized with the reader thread
        /// Reader threads can be concurrent when there is not writers
        /// 
        /// </summary>
        class ReaderWriterProblem
        {
            AutoResetEvent mWRSyncEvent;
            AutoResetEvent mReaderCountEvent;
            CustomResetEvent mWaitHandle;
            int mCurReaderCount = 0;
            int ConcurrencyReaderCount = 1;
            int mWriteTimes = 0;
            bool bStop = false;
            public ReaderWriterProblem(int readercount)
            {
                ConcurrencyReaderCount = readercount;
                mWaitHandle = new CustomResetEvent(ConcurrencyReaderCount+1);
            }

            public void Start()
            {
                mWRSyncEvent = new AutoResetEvent(true);
                mReaderCountEvent = new AutoResetEvent(true);
                Thread writertd = new Thread(Write);
                writertd.Start();
                for (int i = 0; i < ConcurrencyReaderCount; i++)
                {
                    Thread readertd = new Thread(Read);
                    readertd.Start(i);
                }
            }

            public void Stop(int durationforrunning)
            {
                Thread.Sleep(durationforrunning);
                bStop = true;
                mWaitHandle.Wait();
            }
            void Write()
            {
                while (!bStop)
                {
                    mWRSyncEvent.WaitOne();
                    //do writing
                    Console.WriteLine("   {0} The file is be writing... " + (mWriteTimes++),DateTime.Now.ToString("hh:mm:ss fff"));
                    Thread.Sleep(200);
                    // signal that writing is finished
                    Console.WriteLine("Writer exit!");
                    mWRSyncEvent.Set();
                }
                mWaitHandle.SignalFinishOne();
            }

            void Read(object readid)
            {
                while (!bStop)
                {
                    mReaderCountEvent.WaitOne();
                    mCurReaderCount++;
                    if (mCurReaderCount == 1)
                    {
                        mWRSyncEvent.WaitOne();//siganl the file is not able to be written
                    }
                    Console.WriteLine("reader " + readid + " login...");
                    Console.WriteLine("The current reader count is " + mCurReaderCount);
                    mReaderCountEvent.Set();

                    // do reading
                    Console.WriteLine("{0} reader " + readid + " is reading...", DateTime.Now.ToString("hh:mm:ss fff"));
                    Thread.Sleep(100);

                    //exit
                    mReaderCountEvent.WaitOne();
                    mCurReaderCount--;
                    if (mCurReaderCount == 0)
                    {
                        mWRSyncEvent.Set();
                    }
                    Console.WriteLine("reader " + readid + " exit!");
                    Console.WriteLine("The current reader count is " + mCurReaderCount);
                    mReaderCountEvent.Set();
                }
                mWaitHandle.SignalFinishOne();

            }

        }
        
        
    }


}


运行效果:


ReaderWriterLockSlim类提供了可升级读模式,这种方式和读模式的区别在于它还有通过调用EnterWriteLock 或TryEnterWriteLock 方法升级为写入模式。具体例子可以参考msdn

https://msdn.microsoft.com/en-us/library/system.threading.readerwriterlockslim(v=vs.110).aspx

但是通过看《CLR via C#》对该类的解析可以得知,这其实也是一种很耗资源的低效行为。

在此,我们只是通过它来简化实现我们上面的过程。使用该类来实现读者写者问题可以屏蔽了同步及计数器问题。

代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using ThreadUtilities;
namespace ReaderWriter
{
    class Program
    {
        
        static void Main(string[] args)
        {
            //ReaderWriterProblem rwp = new ReaderWriterProblem(3);
            //rwp.Start();
            //rwp.Stop(300);

            ReaderWriterProblemEx rwp = new ReaderWriterProblemEx(3);
            rwp.Start();
            rwp.Stop(300);
        }

        

        class ReaderWriterProblemEx
        {
            ReaderWriterLockSlim mReaderWirterLock;
            CustomResetEvent mWaitHandle;
            int ConcurrencyReaderCount = 1;
            int mWriteTimes = 0;
            bool bStop = false;

            public ReaderWriterProblemEx(int readercount)
            {
                ConcurrencyReaderCount = readercount;
                mWaitHandle = new CustomResetEvent(ConcurrencyReaderCount + 1);
            }
            ~ReaderWriterProblemEx()
            {
                if (mReaderWirterLock != null) mReaderWirterLock.Dispose();
            }
            public void Start()
            {
                mReaderWirterLock = new ReaderWriterLockSlim();
                Thread writertd = new Thread(Writer);
                writertd.Start();
                for (int i = 0; i < ConcurrencyReaderCount; i++)
                {
                    Thread readertd = new Thread(Reader);
                    readertd.Start(i);
                }
            }
            public void Stop(int durationforrunning)
            {
                Thread.Sleep(durationforrunning);
                bStop = true;
                mWaitHandle.Wait();
            }
            void Writer()
            {
                while (!bStop)
                {
                    mReaderWirterLock.EnterWriteLock();
                    Console.WriteLine("    {0} The file is be writing... " + (mWriteTimes++), DateTime.Now.ToString("hh:mm:ss fff"));
                    Thread.Sleep(200);
                    Console.WriteLine("Writer exit!");
                    mReaderWirterLock.ExitWriteLock();
                }
                mWaitHandle.SignalFinishOne();
            }
            void Reader(object readid)
            {
                while (!bStop)
                {
                    Console.WriteLine("{0} reader " + readid + " try to login...", DateTime.Now.ToString("hh:mm:ss fff"));
                    mReaderWirterLock.EnterReadLock();
                    Console.WriteLine("{0} reader " + readid + " login...", DateTime.Now.ToString("hh:mm:ss fff"));
                    // do reading
                    Thread.Sleep(100);
                    Console.WriteLine("{0} reader " + readid + " try to exit", DateTime.Now.ToString("hh:mm:ss fff"));
                    mReaderWirterLock.ExitReadLock();
                    Console.WriteLine("reader " + readid + " exit!");
                }
                mWaitHandle.SignalFinishOne();
            }
        }
        
    }


}


运行效果:

 

存在问题:

以上的实现都是读进程是优先的,也就是说,当存在读进程时,写操作将被延迟,并且只要有一个读进程活跃,随后而来的读进程都将被允许访问文件。这样的方式下,会导致写进程可能长时间等待,且存在写进程“饿死”的情况。

具体解决可以参考

https://en.wikipedia.org/wiki/Readers%E2%80%93writers_problem

http://c.biancheng.net/cpp/html/2601.html

另外参考:

http://blog.csdn.net/morewindows/article/details/7596034

https://www.youtube.com/watch?v=_vfZhdTgA5A3:56

发布了32 篇原创文章 · 获赞 7 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/mochounv/article/details/52291695