在这个信号量示例中,是否需要为refill()和buy()锁定?

2024-10-01 15:34:04 发布

您现在位置:Python中文网/ 问答频道 /正文

在这个信号量示例中,是否需要为refill()和buy()锁定?在

书上说: 当虚拟供应商的所有者- ing机器又增加了一项存货。整个例行程序 表示一个关键部分;这就是为什么获取锁是唯一的方法 执行所有行。在

但我认为没有必要锁定refill()和buy() 你的意见呢?在

#!/usr/bin/env python

from atexit import register
from random import randrange
from threading import BoundedSemaphore, Lock, Thread
from time import sleep, ctime

lock = Lock()
MAX = 5
candytray = BoundedSemaphore(MAX)

def refill():
   # lock.acquire()
    try:
        candytray.release()
    except ValueError:
        pass
    #lock.release()

def buy():

    #lock.acquire()
    candytray.acquire(False)
    #lock.release()

def producer(loops):
    for i in range(loops):
        refill()
        sleep(randrange(3))

def consumer(loops):
    for i in range(loops):
        buy()
        sleep(randrange(3))

def _main():
    print('starting at:', ctime())
    nloops = randrange(2, 6)
    print('THE CANDY MACHINE (full with %d bars)!' % MAX)
    Thread(target=consumer, args=(randrange(nloops, nloops+MAX+2),)).start() # buyer
    Thread(target=producer, args=(nloops,)).start() # vendor

@register
def _atexit():
    print('all DONE at:', ctime())

if __name__ == '__main__':
    _main()

Tags: fromimportlockdefbuysleepthreadmax
3条回答

当然,这是关键部分-你必须锁定它。取消注释那些注释行。在

candytray是一个线程正在为之奋斗的资源。任务的独立性有一个规律:两个任务如果没有相同的密码域,并且第一个任务的域不等于第二个任务的密码域,第二个任务的域不等于第一个任务的密码域。这意味着,两个任务只能从一个内存/变量/等读取

在您的例子中,如果candytray被实现为某个队列,则不需要锁定,因为“writer”将数据放在左侧,“reader”从右侧读取。因此,它们的域和密码子不相等,任务是独立的。 但是,如果它不是queue,如果它是一些,比如说堆,那么writer的codomain就会干扰reader的域。他们是依赖的,你需要锁定。在

编辑

如您所见,我是从理论的角度而不是从Python的角度来讨论的。但我想你是想要的。在

锁是绝对必要的。如果您稍微修改一下代码,以打印每次生产者/消费者呼叫后剩余的糖果数量,可能会有所帮助。替换了信号量,因为它所做的只是保持计数。在

我补充道

numcandies = 5  

对于补充:

^{pr2}$

购买:

def buy():
    global numcandies
    numcandies -= 1
    print("Buy: %d left" %numcandies)

下面是不带锁的输出(显示了数据争用问题)。在

('starting at:', 'Tue Mar 26 23:09:41 2013')
THE CANDY MACHINE (full with 5 bars)!
Buy: 4 left
Refill: 5 left
Refill: 6 left
Buy: 5 left
Buy: 4 left
Buy: 3 left
Refill: 6 left
Refill: 7 left
Buy: 6 left
('all DONE at:', 'Tue Mar 26 23:09:43 2013')

在调用producer和实际更新{}计数器之间的某个地方,我们连续两次调用consumer。在

没有锁定,就无法控制谁实际修改计数器的顺序。所以在上面的例子中,即使numcandies被更新为3buyconsumer,但是{}仍然有一个5的本地副本。更新后,它将计数器设置为6,这是完全错误的。在

来自Wesley Chun的《核心Python应用程序编程》一书中的original code如下所示:

def refill():
    lock.acquire()
    print 'Refilling candy...',
    try:
        candytray.release()
    except ValueError:
        print 'full, skipping'
    else:
        print 'OK'
    lock.release()

def buy():
    lock.acquire()
    print 'Buying candy...',
    if candytray.acquire(False):
        print 'OK'
    else:
        print 'empty, skipping'
    lock.release()

如果没有lock,print语句可能会交织成难以理解的输出。例如,假设糖果托盘已满。然后假设有一个对refill的调用,然后对buy的调用使得代码行按以下顺序执行(不带锁):

^{pr2}$

输出如下所示:

                   # we start with 5 candy bars (full tray)
Refilling candy... # oops... tray is full
Buying candy...    
OK                 # So now there are 4 candy bars
full, skipping     # huh?

这是没有道理的,所以需要一个锁。在

相关问题 更多 >

    热门问题