在多线程环境中我需要锁来保护python 2.7列表吗?

时间:2016-08-31 05:12:04

标签: python multithreading python-2.7

想知道如果多个线程需要访问(读/写/获取大小),我们是否需要锁定Python列表?在Mac上使用Python 2.7。

我编写了一个原型来添加一个锁来保护列表。我的代码中不确定是否有必要或任何问题(性能和功能)?感谢。

顺便说一下,我在Python字典和deque上有同样的问题,关于我们是否需要锁来保护它在多线程环境中。感谢。

import threading
import time
import random

class checkStatus:
    def __init__(self):
        self.message = []
        self.lock = threading.Lock()
    def checkInStatus(self, msg):
        self.lock.acquire()
        self.message.append(msg)
        self.lock.release()
    def checkOutStatus(self):
        self.lock.acquire()
        if len(self.message) > 0:
            msg = self.message.pop(0)
        else:
            msg = 'Queue empty'
        self.lock.release()
        return msg
    def checkMessageStatus(self):
        self.lock.acquire()
        size = len(self.message)
        self.lock.release()
        return size

messageQueue = checkStatus()

class myThread (threading.Thread):
    def __init__(self, threadID, name):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
    def run(self):
        global messageQueue
        while True:
            time.sleep(1+5*random.random())
            print "%s: %s : %s" % (self.name, time.ctime(time.time()), messageQueue.checkMessageStatus())
            time.sleep(1 + 5 * random.random())
            msg = time.ctime(time.time()) + ' ' + self.name
            print "%s: %s : check in message, %s" % (self.name, time.ctime(time.time()), msg)
            messageQueue.checkInStatus(msg)
            time.sleep(1 + 5 * random.random())
            print "%s: %s : check out message, %s" % (self.name, time.ctime(time.time()), messageQueue.checkOutStatus())


if __name__ == "__main__":
    threads = []

    # Create new threads
    thread1 = myThread(1, "Thread-1")
    thread2 = myThread(2, "Thread-2")

    # Start new Threads
    thread1.start()
    thread2.start()

    # Add threads to thread list
    threads.append(thread1)
    threads.append(thread2)

    # Wait for all threads to complete
    for t in threads:
        t.join()
    print "Exiting Main Thread"

输出,

Thread-2: Tue Aug 30 22:08:04 2016 : 0
Thread-1: Tue Aug 30 22:08:05 2016 : 0
Thread-1: Tue Aug 30 22:08:07 2016 : check in message, Tue Aug 30 22:08:07 2016 Thread-1
Thread-2: Tue Aug 30 22:08:07 2016 : check in message, Tue Aug 30 22:08:07 2016 Thread-2
Thread-2: Tue Aug 30 22:08:09 2016 : check out message, Tue Aug 30 22:08:07 2016 Thread-1
Thread-1: Tue Aug 30 22:08:11 2016 : check out message, Tue Aug 30 22:08:07 2016 Thread-2
Thread-2: Tue Aug 30 22:08:11 2016 : 0
Thread-1: Tue Aug 30 22:08:13 2016 : 0
Thread-2: Tue Aug 30 22:08:15 2016 : check in message, Tue Aug 30 22:08:15 2016 Thread-2
Thread-1: Tue Aug 30 22:08:17 2016 : check in message, Tue Aug 30 22:08:17 2016 Thread-1
Thread-2: Tue Aug 30 22:08:18 2016 : check out message, Tue Aug 30 22:08:15 2016 Thread-2
Thread-1: Tue Aug 30 22:08:19 2016 : check out message, Tue Aug 30 22:08:17 2016 Thread-1

1 个答案:

答案 0 :(得分:1)

您的checkOutStatus方法需要锁才能正常工作;其他方法没有,因为它们正在执行原子操作(简单的Python语句是原子的,请参阅此reference)。如果没有checkOutStatus中的锁定,则在使用self.message.pop(0)检索消息之前,可能存在if语句的计算结果为True但是立即发生线程切换的情况。如果第二个线程然后删除该消息,则当第一个线程继续时,它将尝试从空列表中弹出。如果您按如下方式重写函数:

def checkOutStatus(self):
    try:
        msg = self.message.pop(0)
    except IndexError:
        msg = 'Queue empty'
    return msg

它也是线程安全的,因为唯一的操作是原子的。在这种情况下,您可以删除所有锁定代码。