- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我的模块中有两个功能:do_something()
, 和 change_behavior()
.
函数do_something()
默认情况下执行 A。后 change_behavior()
已被调用,do_something()
而是做 B 事情。
我希望这种转换是特定于线程的。也就是说,任何新线程在调用 do_something()
时都会发生事件 A。 ,但如果该线程调用 change_behavior()
,那么当 B 继续调用 do_something()
时,它会发生。 .
每个线程应该是独立的,这样一个线程调用 change_behavior()
不影响 do_something()
的行为对于其他线程。
我对此的本能解决方案是将行为与线程的 ID 相关联(通过 threading.get_ident()
评估)。函数do_something()
检查本地表中是否存在线程的 ID,并相应地调整其行为。同时,函数change_behavior()
只需将当前线程添加到该注册表中即可。这在任何给定时间都有效,因为永远不会有两个具有相同 ID 的并发线程。
当当前线程集加入时,问题就出现了,随着时间的流逝,父线程创建了更多线程。新线程之一与先前线程之一具有相同的 ID,因为有时会重复使用线程 ID。该线程调用 do_something()
,并且因为它已经在注册表中,所以它执行的是 B 而不是 A。
为了解决这个问题,我需要以某种方式从注册表中删除线程 ID,在具有该 ID 的第一个线程结束和具有该 ID 的第二个线程开始之间。我提出的一些假设性想法:
change_behavior()
的一部分, 劫持/替换当前线程的 ._quit()
方法首先从注册表中删除线程的 ID。这似乎是不好的做法,并且可能会破坏。 threading.local()
@TarunLalwani 在评论中提出了建议。我已经调查过了,它很有用,但它没有考虑到我想要处理的其他用例 - 当父线程创建新的子线程时,我希望它们继承父线程的状态.我想通过替换
Thread.__init__()
来实现这一点。 ,但使用
local()
通常与此用例不兼容,因为我无法将变量从父线程传递到子线程。
current_thread = threading.current_thread()
setattr(current_thread, my_reference, new_value)
current_thread.my_reference
也设置为
new_value
.我不知道为什么,而且我一直无法在 MVE 中复制这个问题(尽管它在我的 IDE 中一直发生,即使在重新启动它之后)。如
my other currently-active question暗示,我在这里设置的对象是对输出流的引用(我在该答案中描述的对中间 IO 流实例的每个引用都被调用此方法的文件描述符替换),如果有的话与它有关,但我无法想象为什么对象的类型会影响引用在这种情况下的工作方式。
最佳答案
我的回答是对你问题的一个非常简单的回答,因此我想知道我是否遗漏了什么。基本上,我认为您应该避免在模块中存储外部对象的当前状态。
您需要在某处存储状态(如果 change_behavior
被调用,也许还有一些其他数据)。您有两个主要选择:将状态存储在模块中或将状态存储在线程本身中。除了您在模块中存储状态时遇到的问题之外,人们希望模块(主要)是无状态的,因此我认为您应该坚持后者并将数据存储在线程中。
版本 1
如果您将状态存储在字段中,您创建的属性名称和现有属性名称之间会有一点冲突的风险,但是如果文档很清楚并且您选择了一个好的名称,那么这不应该是一个问题。
一个简单的概念证明,没有 setattr
或 hasattr
(我没有检查 CPython 的源代码,但也许奇怪的行为来自 setattr
):
模块1.py
import threading
import random
import time
_lock = threading.Lock()
def do_something():
with _lock:
t = threading.current_thread()
try:
if t._my_module_s:
print(f"DoB ({t})")
else:
print(f"DoA ({t})")
except AttributeError:
t._my_module_s = 0
print(f"DoA ({t})")
time.sleep(random.random()*2)
def change_behavior():
with _lock:
t = threading.current_thread()
print(f"Change behavior of: {t}")
t._my_module_s = 1
import random
import threading
from module1 import *
class MyThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
n = random.randint(1, 10)
for i in range(n):
do_something()
change_behavior()
for i in range(10-n):
do_something()
thread_1 = MyThread()
thread_2 = MyThread()
thread_1.start()
thread_2.start()
thread_1.join()
thread_2.join()
DoA (<MyThread(Thread-1, started 140155115792128)>)
DoA (<MyThread(Thread-2, started 140155107399424)>)
DoA (<MyThread(Thread-1, started 140155115792128)>)
DoA (<MyThread(Thread-1, started 140155115792128)>)
Change behavior of: <MyThread(Thread-1, started 140155115792128)>
DoB (<MyThread(Thread-1, started 140155115792128)>)
DoB (<MyThread(Thread-1, started 140155115792128)>)
DoA (<MyThread(Thread-2, started 140155107399424)>)
DoB (<MyThread(Thread-1, started 140155115792128)>)
DoA (<MyThread(Thread-2, started 140155107399424)>)
DoB (<MyThread(Thread-1, started 140155115792128)>)
DoA (<MyThread(Thread-2, started 140155107399424)>)
DoA (<MyThread(Thread-2, started 140155107399424)>)
DoB (<MyThread(Thread-1, started 140155115792128)>)
DoA (<MyThread(Thread-2, started 140155107399424)>)
Change behavior of: <MyThread(Thread-2, started 140155107399424)>
DoB (<MyThread(Thread-2, started 140155107399424)>)
DoB (<MyThread(Thread-1, started 140155115792128)>)
DoB (<MyThread(Thread-1, started 140155115792128)>)
DoB (<MyThread(Thread-2, started 140155107399424)>)
DoB (<MyThread(Thread-2, started 140155107399424)>)
DoB (<MyThread(Thread-2, started 140155107399424)>)
Thread
的所有者子类和您避免名称冲突的风险。另外,在我看来,代码变得更干净了:
import threading
import random
import time
_lock = threading.Lock()
def do_something():
with _lock:
t = threading.current_thread()
t.do_something() # t must be a _UserFunctionWrapper
time.sleep(random.random()*2)
def change_behavior():
with _lock:
t = threading.current_thread()
t.change_behavior() # t must be a _UserFunctionWrapper
def wrap_in_thread(f):
return _UserFunctionWrapper(f)
class _UserFunctionWrapper(threading.Thread):
def __init__(self, user_function):
threading.Thread.__init__(self)
self._user_function = user_function
self._s = 0
def change_behavior(self):
print(f"Change behavior of: {self}")
self._s = 1
def do_something(self):
if self._s:
print(f"DoB ({self})")
else:
print(f"DoA ({self})")
def run(self):
self._user_function()
import random
from module2 import *
def user_function():
n = random.randint(1, 10)
for i in range(n):
do_something() # won't work if the function is not wrapped
change_behavior()
for i in range(10-n):
do_something()
thread_1 = wrap_in_thread(user_function)
thread_2 = wrap_in_thread(user_function)
thread_1.start()
thread_2.start()
thread_1.join()
thread_2.join()
DoA (<_UserFunctionWrapper(Thread-1, started 140193896072960)>)
DoA (<_UserFunctionWrapper(Thread-2, started 140193887680256)>)
DoA (<_UserFunctionWrapper(Thread-2, started 140193887680256)>)
Change behavior of: <_UserFunctionWrapper(Thread-1, started 140193896072960)>
DoB (<_UserFunctionWrapper(Thread-1, started 140193896072960)>)
DoB (<_UserFunctionWrapper(Thread-1, started 140193896072960)>)
DoA (<_UserFunctionWrapper(Thread-2, started 140193887680256)>)
DoA (<_UserFunctionWrapper(Thread-2, started 140193887680256)>)
DoB (<_UserFunctionWrapper(Thread-1, started 140193896072960)>)
DoB (<_UserFunctionWrapper(Thread-1, started 140193896072960)>)
DoA (<_UserFunctionWrapper(Thread-2, started 140193887680256)>)
DoB (<_UserFunctionWrapper(Thread-1, started 140193896072960)>)
DoA (<_UserFunctionWrapper(Thread-2, started 140193887680256)>)
DoB (<_UserFunctionWrapper(Thread-1, started 140193896072960)>)
DoA (<_UserFunctionWrapper(Thread-2, started 140193887680256)>)
DoB (<_UserFunctionWrapper(Thread-1, started 140193896072960)>)
DoA (<_UserFunctionWrapper(Thread-2, started 140193887680256)>)
DoA (<_UserFunctionWrapper(Thread-2, started 140193887680256)>)
Change behavior of: <_UserFunctionWrapper(Thread-2, started 140193887680256)>
DoB (<_UserFunctionWrapper(Thread-2, started 140193887680256)>)
DoB (<_UserFunctionWrapper(Thread-1, started 140193896072960)>)
DoB (<_UserFunctionWrapper(Thread-1, started 140193896072960)>)
关于python - 设置当线程以其他方式完成时要执行的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56959718/
我是一名优秀的程序员,十分优秀!