2

在这个 Semaphore 示例中,是否需要锁定 refill() 和 buy() ?

这本书说:当虚拟自动售货机的所有者来向库存中添加一件物品时,执行 refill() 函数。整个例程代表一个关键部分;这就是为什么获取锁是执行所有行的唯一方法。

但我认为没有必要锁定 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()
4

3 回答 3

1

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

我添加了

numcandies = 5  

补充:

def refill():
    global numcandies
    numcandies += 1
    print ("Refill: %d left" % numcandies) 

购买:

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')

在调用 ofproducer和实际更新numcandies计数器之间的某个地方,我们连续调用了 2 次consumer.

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

于 2013-03-27T03:20:51.397 回答
0

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

candytray是线程挣扎的资源。有关于任务独立性的规则:如果两个任务没有相同的域并且第一个域不等于第二个域并且第二个域不等于第一个域,则它们是独立的。这意味着,两个任务只能从一个内存/变量/等中读取。

在您的情况下,如果candytray实现为某个队列,则不需要锁定,因为“写入器”将数据放在左侧,而“读取器”从右侧读取。因此,它们的域和共域不相等,并且该任务是独立的。但是,如果不是队列,如果是一些,比如说堆,那么作者的域会干扰读者的域。它们是依赖的,你需要锁定。

编辑

如您所见,我是从理论方面讲的,而不是 Python 方面。但我猜你想要那个。

于 2013-03-27T03:14:23.773 回答
0

来自 Wesley Chun 的《Core Python Applications Programming 》一书的原始代码如下所示:

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,打印语句可能会交织成难以理解的输出。例如,假设糖果盘已满。然后假设有一个调用,refill然后是一个调用,buy使得代码行按此顺序执行(没有锁):

print 'Refilling candy...',

print 'Buying candy...',    

try:
    candytray.release()

if candytray.acquire(False):
    print 'OK'

except ValueError:
    print 'full, skipping'

输出将如下所示:

                   # 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?

那没有意义,所以需要锁。

于 2013-03-27T03:17:13.550 回答