天天看点

读者写者问题(写者优先)的Python实现,以售票为例

读者写者问题是操作系统领域一个重要的问题。分为读者优先与写者优先两种。

通过加锁的方式,实现对临界资源的访问控制。

下面以写者优先为例子,以Python为实现方式实现读者写者问题。

辅助类:随机一个票号

class CreateRandomPage:
    # get a random list which use num that
    # between self.begin and self.end
    # and length that is self.needcount
    def __createrandompage(self):
        import random
        tempInt = random.randint(self.begin, self.end)
        if self.count < self.needcount:
            if tempInt not in self.resultlist:
                self.resultlist.append(tempInt)  # 将产生的随机数追加到列表中
                self.count += 1
            return self.__createrandompage()  # 在此用递归的思想
        return self.resultlist

    # list2str
    def __getRandomPageNumber(self):
        self.resultlist = []
        self.count = 0
        return ''.join(map(str,self.__createrandompage()))

    # num为随机数组的长度 随机出一个10位的包含0-9的数字
    def getRandomPageList(self,num):
        self.begin = 0
        self.end   = 9
        self.needcount = 10
        ans = []
        for i in range(num):
            str = self.__getRandomPageNumber()
            ans.append(str)

        ans = list(set(ans))

        for j in range(len(ans),num):
            str = self.__getRandomPageNumber()
            if str not in ans:
                ans.append(str)
            else:
                j-=1
        ans.sort()
        return ans           

读者写者问题关键代码:

import time
import random
import Practice.Practice3.RandomIndex as Ran
import threading

class Ticket(object):
    # This is a writer-reader problem
    # use writer priority
    # it means that if a writer come and want to write
    # after the last reader read the data
    # the writer can cut in line before all readers that not in critical resource
    # also, if a writer read now
    # a reader can read when all writers have writed
    def __init__(self,num):
        self.accessReaderCnt = threading.Lock()
        self.accessWriterCnt = threading.Lock()
        self.writeLock       = threading.Lock()
        self.readerLock      = threading.Lock()
        self.outerLock       = threading.Lock()
        self.readerCnt       = 0
        self.writerCnt       = 0
        ran = Ran.CreateRandomPage()
        self.tickets = ran.getRandomPageList(num)

    def __write(self,tname):
        sum = len(self.tickets)
        if sum<=0:
            print("{}:没票买了,俺先溜了!".format(tname))
            return str(sum)
        ticket = self.tickets.pop(random.randint(0,sum-1))
        print("{}:购票成功!票号为{}!".format(tname, ticket))
        return ticket

    def __read(self,tname):
        sum = len(self.tickets)
        if sum<=0:
            print("{}:没票买了,俺先溜了!".format(tname))
            return str(sum)
        print("{} 当前还有{}张票,我马上就来买!".format(tname, sum))
        return str(sum)

    def Query(self,tname):
        # let reader in queue
        # the outerLock is the most IMPORTANT lock
        # think this situation
        # if there are many readers,no writer
        # at some time, a writer come
        # if the outerLock exist,there is only one reader in outerLock
        # reader unlock readerLock faster than outerLock
        # you can look the writer, it can access accessWriterCnt (Look at line 91),
        # and wait to lock readerLock(read wait at readerLock)
        # because the accessReaderCnt unlock faster than readerLock
        # so if a writer come, writer can fastly get readerLock, and lock it
        # it means that writer can access critical resource faster than many readers
        with self.outerLock:#996
            # look if writer lock readerLock
            # if readerLock is none,reader begin to read
            with self.readerLock:
                # change reader num, lock it
                with self.accessReaderCnt:#1
                    self.readerCnt+=1
                    if self.readerCnt==1:
                        # if some reads reading now
                        # writer must wait
                        # so lock writeLock
                        self.writeLock.acquire()

        # read operation
        ans = self.__read(tname)

        # change reader num, lock it
        with self.accessReaderCnt:
            self.readerCnt-=1
            # there is no reader reading now
            # so unlock writeLock
            if self.readerCnt==0:
                self.writeLock.release()

        time.sleep(random.randint(1,100)/1000)
        return ans


    # Sell tricket
    def Sell(self,tname):
        # accessWriterCnt control writerCnt number change
        with self.accessWriterCnt:
            self.writerCnt+=1
            # has writers, lock readerLock,
            # readers which not in readerLock will can't get readerLock
            if self.writerCnt==1:
                self.readerLock.acquire()


        # write want to write, lock it
        with self.writeLock:
            # write operation
            ans = self.__write(tname)


        # want to change writerCnt, lock it
        with self.accessWriterCnt:
            self.writerCnt-=1
            # writer's number is zero
            # stand for no writers now
            # so readers can read now, unlock readerLock
            if self.writerCnt==0:
                self.readerLock.release()

        time.sleep(random.randint(1,100)/1000)
        return ans
           

辅助类,顾客:

import random
import time
import threading
import Practice.Practice3.Ticketer as Ticket

class Customers(object):
    def __init__(self,num):
        self.num=num

    # 线程任务 tname线程名 ticketer售票处
    def __threadTask(self,tname,ticketer):
        while True:
            # 随机一个数 每个线程都能去买票或者问询 25%几率买,75%几率问
            opt = random.random()
            if opt>0.75:
                result = ticketer.Sell(tname)
                if result == "0":
                    break
            else:
                result = ticketer.Query(tname)
                if result == "0":
                    break
            time.sleep(random.randint(1000,1500)/1000)

    # 线程运行 num运行数目 ticketer售票处
    def Run(self,ticketer):
        tname=["客户"]*self.num
        threadList = []
        for i in range(self.num):
            tname[i]+=str((i+1))
            new_thread = threading.Thread(target=self.__threadTask, args=(tname[i],ticketer,))
            threadList.append(new_thread)
        numList = [a for a in range(self.num)]
        random.shuffle(numList)
        for i in numList:
            threadList[i].start()

if __name__ == '__main__':
    ticketer = Ticket.Ticket(int(input("票站票数:")))
    threads = Customers(int(input("顾客人数:")))
    threads.Run(ticketer)