Python:在没有Lock机制的情况下,通过多个线程同时访问函数

时间:2013-03-14 06:15:47

标签: python multithreading function locking

当多个线程访问同一个函数时,我们是否需要明确地实现 lock 机制。

我有一个使用线程的程序。 有两个帖子t1t2t1用于add1()t2用于subtract1()。两个线程同时访问同一个函数myfunction(caller,num)

1。我使用变量functionLock在给定程序中定义了一个简单的锁机制。这是可靠的还是我们需要修改它。

import time, threading

functionLock = '' # blank means lock is open        

def myfunction(caller,num):
    global functionLock
    while functionLock!='': # check and wait until the lock is open
        print "locked by "+ str(functionLock)
        time.sleep(1)

    functionLock = caller # apply lock

    total=0
    if caller=='add1':
        total+=num
        print"1. addition finish with Total:"+str(total)
        time.sleep(2)
        total+=num
        print"2. addition finish with Total:"+str(total)
        time.sleep(2)
        total+=num
        print"3. addition finish with Total:"+str(total)

    else:
        time.sleep(1)
        total-=num
        print"\nSubtraction finish with Total:"+str(total)

    print '\n For '+caller+'() Total: '+str(total)

    functionLock='' # release the lock


def add1(arg1, arg2):

    print '\n START add'
    myfunction('add1',10)
    print '\n END add'        


def subtract1():

  print '\n START Sub'  
  myfunction('sub1',100)   
  print '\n END Sub'


def main():

    t1 = threading.Thread(target=add1, args=('arg1','arg2'))
    t2 = threading.Thread(target=subtract1)
    t1.start()
    t2.start()


if __name__ == "__main__":
  main()

输出如下:

START add
START Sub
1. addition finish with Total:10
locked by add1
locked by add1
2. addition finish with Total:20
locked by add1
locked by add1
3. addition finish with Total:30 
locked by add1
 For add1() Total: 30
 END add
Subtraction finish with Total:-100
 For sub1() Total: -100
 END Sub

2。我们不使用锁吗?

即使我不使用上述程序中定义的锁定机制,线程t1和t2的结果也相同。这是否意味着当多个线程访问同一个函数时,python会自动实现锁定。

在上述程序

中不使用锁functionLock的程序输出
START add
START Sub
1. addition finish with Total:10
Subtraction finish with Total:-100
For sub1() Total: -100
END Sub
2. addition finish with Total:20
3. addition finish with Total:30
For add1() Total: 30
END add

谢谢!

4 个答案:

答案 0 :(得分:1)

虽然我对Python知之甚少,但我会说这与任何其他语言一样:

只要没有涉及已在函数外部声明的变量并且因此可以在线程之间共享,就不需要锁定。你的功能似乎并非如此。

但是输出到控制台可能会出现乱码。

答案 1 :(得分:1)

当您认为正在编写的代码是关键部分代码时,您需要锁定,即代码片段是否正在修改线程之间的共享状态,如果不是,则您不需要担心锁定。

是否应该锁定方法是一种设计选择,理想情况下,您应该更接近线程的共享状态访问锁定。

答案 2 :(得分:1)

  1. 在您的代码中,您实现了自己的spin-lock。虽然这是可能的,但我不认为它是在Python中推荐的,因为它可能会导致性能问题。

  2. 我使用了一个众所周知的搜索引擎(以G开头),查询" python lock"。在第一个结果是这一个:Thread Synchronization Mechanisms in Python。它看起来是一篇很好的文章。

  3. 对于代码本身:只要在共享资源上执行的操作不是原子操作,就应该锁定。目前看来你的代码中没有这样的资源。

答案 3 :(得分:1)

除了关于忙于等待变量的这个线程的其他注释之外,我想指出你没有使用任何类型的原子交换这一事实可能会导致并发错误。即使您的测试执行不会导致它们出现,如果执行了不同时间的重复,可能会出现以下事件序列:

线程#1执行while functionLock!=''并获得False。然后,线程#1被中断(抢占其他要执行的东西),线程#2执行同一行,while functionLock!=''也获得False。在这个例子中,两个线程都已进入临界区,这显然不是你想要的。特别是,在线程修改total的任何行中,结果可能不是您期望的结果,因为两个线程可以同时在该部分中。请参阅以下示例:

total10。为简单起见,假设num始终为1.线程#1执行total+=num,它由三个操作组成:(i)加载total的值,(ii)添加它num和(iii)将结果存储在total中。如果在(i)之后,线程#1被抢占,而线程#2则执行total-=numtotal被设置为9。然后,线程#1重新开始。但是,它已加载total = 10,因此它会添加1并将11存储到total变量中。这有效地改变了线程#2在无操作中的减量操作。

请注意,在由@ ron-klein链接的维基百科文章中,代码使用xchg操作,该操作以原子方式将寄存器与变量交换。这对于修正锁定至关重要。总之,如果你想避免难以调试的并发错误,那么永远不要将自己的锁实现为原子操作的替代。

[edit]我刚才注意到,事实上total是代码中的局部变量,所以这种情况永远不会发生。但是,我相信你并不知道这是你完美工作的代码的原因,因为你肯定“这是否意味着当多个线程访问同一个函数时python会自动实现锁定。”,这不是真的。请尝试将global total添加到myfunction的开头,并多次执行线程,您应该会在输出中看到错误。 [/编辑]