如何在python中的线程之间安全地传递变量值

All*_*hek 1 python queue multithreading

我在python文档中读到Queue.Queue()是一种在不同线程之间传递变量的安全方法.我真的不知道多线程存在安全问题.对于我的应用程序,我需要使用可以从多个不同线程访问的变量开发多个对象.现在我只是让线程直接访问对象变量.我不会在这里显示我的代码,因为它的方式太多了,但这里有一个例子来展示我正在做的事情.

from threading import Thread
import time
import random

class switch:
    def __init__(self,id):
        self.id=id
        self.is_on = False

    def self.toggle():
        self.is_on = not self.is_on

switches = []
for i in range(5):
switches[i] = switch(i)

def record_switch():
    switch_record = {}
    while True:
        time.sleep(10)
        current = {}
        current['time'] = time.srftime(time.time())
        for i in switches:
            current[i.id] = i.is_on
        switch_record.update(current)

def toggle_switch():
    while True:
        time.sleep(random.random()*100)
        for i in switches:
            i.toggle()

toggle = Thread(target=toggle_switch(), args = ())
record = Thread(target=record_switch(), args = ())

toggle.start()
record.start()
Run Code Online (Sandbox Code Playgroud)

据我所知,队列对象只能用于放置和获取值,这显然不适用于我.我在这里"安全"吗?如果没有,我该如何编程,以便我可以安全地从多个不同的线程访问变量?

The*_*nse 7

每当你有线程修改其他线程可以看到的值时,你就会遇到安全问题.担心的是,当另一个线程正在修改它时,线程将尝试修改一个值,该线程具有风险和未定义的行为.所以不,你的切换切换代码是不安全的.

重要的是要知道改变变量的值不能保证是原子的.如果某个操作是原子操作,则意味着操作将始终在一个不间断的步骤中发生.(这与数据库定义略有不同.)更改变量值(尤其是列表值)通常可以在处理器级别上执行多个步骤.当您使用线程时,所有这些步骤都不能保证在另一个线程开始工作之前一次完成.x当线程B突然接管时,线程A完全有可能在改变变量的中途.然后,如果线程B尝试读取变量x,它将不会找到正确的值.更糟糕的是,如果线程B尝试修改变量,x而线程A在做同样的事情的过程中,可能会发生不好的事情.每当你有一个值可以某种方式改变的变量时,所有对它的访问都需要成为线程安全的.

如果要修改变量而不是传递消息,则应该使用Lock对象.

在您的情况下,您Lock在顶部有一个全局对象:

from threading import Lock

switch_lock = Lock()
Run Code Online (Sandbox Code Playgroud)

然后,您将使用acquirerelease函数包围关键代码段.

    for i in switches:
        switch_lock.acquire()
        current[i.id] = i.is_on
        switch_lock.release()

    for i in switches:
        switch_lock.acquire()
        i.toggle()
        switch_lock.release()
Run Code Online (Sandbox Code Playgroud)

只有一个线程可能一次获得一个锁(无论如何这种锁).当任何其他线程尝试时,它们将被阻止并等待锁再次释放.因此,通过在代码的关键部分放置锁定,您可以使多个线程无法随时查看或修改给定的开关.你可以把它放在你希望一次保留为一个线程的任何代码中.

编辑:正如马蒂诺指出的那样,with如果您使用的是具有它的Python版本,则锁与语句很好地集成在一起.如果发生异常,这具有自动解锁的额外好处.所以不是上面acquirerelease系统,你可以这样做:

    for i in switches:
        with switch_lock:
            i.toggle()
Run Code Online (Sandbox Code Playgroud)

  • 如果`switch`类只是用于多线程设置,那么将锁定在对象中并将锁定逻辑放入`toggle`本身可能是有意义的.读取一个`bool`总是原子的,所以除非交换机中的数据变得更复杂(多个字段都可以更改),否则不需要在编写器和读取器之间进行同步.此外,您应该使用`with`语句以异常安全的方式处理锁定和解锁. (2认同)