【问题标题】:Globally-scoped variable: can its value change before it is picked up by the thread?全局范围的变量:它的值在被线程拾取之前可以改变吗?
【发布时间】:2009-08-04 16:49:40
【问题描述】:

在下面的代码中,您看到pickledList 正在被线程使用并设置在全局范围内。

如果线程使用的变量是在最后的 while 循环中动态设置的,那么它的值是否有可能在线程开始使用它之前改变?如何在循环中动态设置值,将其发送到线程并确保在线程开始使用它之前其值不会改变?

import pickle
import Queue
import socket
import threading

someList = [ 1, 2, 7, 9, 0 ]
pickledList = pickle.dumps ( someList )

class ClientThread ( threading.Thread ):
   def run ( self ):
      while True:
         client = clientPool.get()
         if client != None:
            print 'Received connection:', client [ 1 ] [ 0 ]
            client [ 0 ].send ( pickledList )
            for x in xrange ( 10 ):
               print client [ 0 ].recv ( 1024 )
            client [ 0 ].close()
            print 'Closed connection:', client [ 1 ] [ 0 ]


clientPool = Queue.Queue ( 0 )

for x in xrange ( 2 ):
   ClientThread().start()

server = socket.socket ( socket.AF_INET, socket.SOCK_STREAM )
server.bind ( ( '', 2727 ) )
server.listen ( 5 )

while True:
   clientPool.put ( server.accept() )

编辑:

这是我的问题的一个更好的例子。如果你运行它,有时值会在线程输出它们之前发生变化,导致一些被跳过:

from threading import Thread
class t ( Thread ):
    def run(self):
        print "(from thread) ",
        print i

for i in range(1, 50):    
    print i
    t().start()

如何将i 中的值传递给线程,使其不再绑定到变量,这样如果i 中存储的值发生变化,则线程正在使用的值不是受影响。

【问题讨论】:

  • 它们是完全不同的问题,引用相同的源列表。在第一个问题中,我不了解范围。在这个问题中,我不明白如何保护传递给线程的变量中的数据。
  • “我怎样才能在那个循环中动态设置一个值,将它发送到线程并确保它的值在线程开始使用它之前不会改变?”这意味着什么?在线程访问它之前,该值不能改变?
  • 要么,要么我需要将值发送到线程,使其最终处于一个新范围内,在该范围内更改原始值不会影响线程正在使用的值
  • 这不是很清楚。如果线程不应该使用它,为什么还要向它发送一个新值?
  • 问题是在我正在查看的示例中,该值从未发送到线程。线程正在使用全局范围内的变量。我的问题是,如果我调用一个线程以使用存储在该变量中的值,但值在线程有机会使用它之前发生了变化,该怎么办。我的问题是,我将如何以改变初始变量中的值不会影响线程正在使用的值的方式将该变量中的值传递给线程。

标签: python multithreading scope


【解决方案1】:

选项1:您可以在实例化每个线程时将参数传递给它:

ClientThread(arg1, arg2, kwarg1="three times!").start()

在这种情况下,您的 run 方法将被调用:

run(arg1, arg2, kwarg1="three times!")

当您调用 start() 时,由 Thread 实例调用。如果您需要将可变对象(dicts、lists、instances)传递给函数,您必须确保重新分配全局变量,不要就地编辑它。

选项 2:您可以在 ClientThread 对象上设置实例变量:

myThread.setMyAttribute('new value')

使用选项 2,您需要警惕竞争条件等,具体取决于方法的作用。每当您在线程中写入或读取数据时使用Locks 是个好主意。

选项 3:在首次调用 run 时获取全局并在本地存储副本

run(self):
    localVar = globalVar # only for immutable types
    localList = globalList[:] # copy of a list
    localDict = globalDict.copy() # Warning! Shallow copy only!

如果线程的值在其生命周期内永远不需要更改,选项 1 是正确的方法,如果值确实需要更改,选项 2 是正确的方法。选项 3 是一个 hack。

关于一般传递变量/值,您必须记住,在 Python 中,不可变对象(字符串、数字、元组)按值传递,而可变对象(字典、列表、类实例) 通过引用传递

因此,更改字符串、数字或元组不会影响之前传递该变量的任何实例,但是更改 dict、列表或实例会起作用。将变量重新分配给不同的对象不会影响以前给定旧值的任何内容。

全局变量当然不应该用于可能改变(如果有的话)的值。基本上你的例子做错了。

【讨论】:

  • 感谢您在那儿向我展示 :) 非常有帮助的答案。
  • 好的,你的回答很有道理,但又引出了另一个问题。我正在使用选项 1,但我不知道在使用 Queueing 时如何将这些值传递给线程。
  • 线程被创建并添加到池中,甚至在生成任何要放入线程的相关数据之前。可以吗?之后我可以以某种方式将数据传递到这些线程中,使用 Queue 将该数据放入已经进入线程池的线程中吗?
  • 您可以使用队列来做到这一点,但您实际上并不需要队列。如果在创建线程时传入的值不够,您应该使用选项 2。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-06-12
  • 2020-05-03
  • 2020-11-09
  • 2013-05-26
相关资源
最近更新 更多