3

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

我有一个使用线程的程序。有两个线程,t1t2t1is for add1()and t2is for 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

4 回答 4

1

虽然我不太了解 Python,但我想说这就像任何其他语言一样:

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

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

于 2013-03-14T06:19:44.540 回答
1

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

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

于 2013-03-14T06:20:31.753 回答
1
  1. 在您的代码中,您实现自己的自旋锁。虽然这是可能的,但我认为在 Python 中不建议这样做,因为它可能会导致性能问题。

  2. 我使用了一个著名的搜索引擎(以 开头G),查询“python lock”。第一个结果之一是:Python 中的线程同步机制。它看起来像是一篇不错的文章。

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

于 2013-03-14T06:34:59.680 回答
1

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

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

total10。为简单起见,假设num始终为 1。 Thread#1 执行total+=num,它由三个操作组成:(i) 加载 的值total,(ii) 将其相加,num以及 (iii) 将结果存储在 中total。如果在 (i) 之后,Thread#1 被抢占并且 Thread#2 然后执行total-=numtotal则设置为9。然后,线程#1 恢复。但是,它已经加载total = 10了 ,所以它加 1 并存储11total变量中。这有效地改变了 Thread#2 在无操作中的递减操作。

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

[编辑] 我刚刚注意到它实际上total是你代码中的一个局部变量,所以这永远不会发生。但是,我相信您没有意识到这是您的代码完美运行的原因,因为您确认“这是否意味着当多个线程访问同一函数时,python 会自动实现锁。”,这是不正确的。请尝试添加global total到开头myfunction,并多次执行线程,您应该会在输出中看到错误。[/编辑]

于 2013-03-14T17:44:22.813 回答