python中的线程锁不能按预期工作

时间:2014-05-21 12:48:16

标签: python multithreading locking

我正在尝试保护线程中的数据不受主线程的影响。我有以下代码:

lock = threading.Lock()

def createstuff(data):
    t= threading.Thread(target=func, args=(data,))
    t.start()

def func(val):
    with lock:
        print 'acquired'
        time.sleep(2)
        print ('Value: %s, : %s'%(val, threading.currentThread().getName()))
        print 'released\n'

ags_list = ['x']

createstuff(ags_list)

rn =random.randint(5,50)
print 'random no:', rn
ags_list[0] = rn 

它产生输出:

acquired
random no: 10
Value: [10], : Thread-1
released

为什么更改主线程中的列表会导致另一个线程内的列表变异,即使它被锁定了?我该怎么做才能防止它?感谢。

2 个答案:

答案 0 :(得分:2)

在python list中通过引用传递,因此list上的任何更改都会反映在list正在使用的任何其他地方。

这是一个link文档,可能有助于清除更多内容。

lock = threading.Lock()

def createstuff(data):
    t= threading.Thread(target=func, args=(data,))
    t.start()

def func(val):
    with lock:
        print 'acquired'
        time.sleep(2)
        print ('Value: %s, : %s'%(val, threading.currentThread().getName()))
        print 'released\n'

ags_list = ['x']
# you would need to create a copy of different copy of that list
new_ags_list = ags_list[:] #here 

createstuff(ags_list)

rn =random.randint(5,50)
print 'random no:', rn
ags_list[0] = rn 

答案 1 :(得分:2)

因为如果你在任何地方使用你正在改变列表的锁定只会有效,如果你只在一个地方调用它,它就不是一个适用于任何地方的魔法咒语。

要保护列表,您需要在两个线程上添加锁定上下文:

lock = threading.Lock()

def createstuff(data):
    t= threading.Thread(target=func, args=(data,))
    t.start()

def func(val):
    with lock:
        print 'thread: acquired'
        time.sleep(2)
        print ('Value: %s, : %s'%(val, threading.currentThread().getName()))
        print 'thread released'

ags_list = ['x']


createstuff(ags_list)

with lock:
    print 'thread: acquired'
    rn =random.randint(5,50)
    print 'random no:', rn
    ags_list[0] = rn 
    print 'thread: released'

您可以创建一个线程安全列表,例如:

class ThreadSafeList(list):
    def __init__(self, *args):
        super(ThreadSafeList, self).__init__(*args)
        self.lock = threading.Lock()
    def __setitem__(self, idx, value):
        with self.lock:
            print 'list acquired'
            super(ThreadSafeList, self)[idx] = value
            print 'list released'

然后使用它:

def createstuff(data):
    t= threading.Thread(target=func, args=(data,))
    t.start()

def func(val):
    time.sleep(2)
    print ('Value: %s, : %s'%(val, threading.currentThread().getName()))

args_list = ThreadSafeList(['x'])
createstuff(args_list)
rn =random.randint(5,50)
print 'random no:', rn
args_list[0] = rn 

当然这只是一个需要完成和改进的例子。在这里,我更倾向于关注这一点。

虽然您不需要在线程中使用锁,但是因为从列表中访问值是(afaict)原子只读操作,所以列表的变异只能在访问值之前或之后发生。列表,而不是访问该值。所以最后你不应该在你的例子中遇到任何种族问题。

如果您正在修改列表的值,或者对数据进行非原子访问,那么锁定可能很有用。

N.B。:如果您认为它可以以任何其他方式工作:互斥机制(通过Lock实现)不保护数据,它保护两个执行线程不会同时执行。如果在线程A 中声明锁定,则在线程B 中声明相同的锁定之前,线程B 将等待线程A < / em>取消锁定直到完成其工作。