【发布时间】:2019-06-30 07:11:45
【问题描述】:
环境:
Ubuntu 18.04
Python 3.6.6
这是一个代码示例:
import threading
import asyncio
from concurrent.futures import ThreadPoolExecutor
executor = ThreadPoolExecutor(3)
def thread_run():
i = 0
for x in range(10):
i += 1
print(x, threading.get_ident())
if __name__ == '__main__':
loop = asyncio.get_event_loop()
for x in range(2):
loop.run_in_executor(executor, thread_run)
loop.run_forever()
输出:
0 140522512643840
1 140522512643840
2 140522512643840
3 140522512643840
4 140522512643840
5 140522512643840
6 140522512643840
7 140522512643840
0 140522504251136
1 140522504251136
2 140522504251136
3 140522504251136
4 140522504251136
5 140522504251136
6 140522504251136
7 140522504251136
8 140522504251136
9 140522504251136
8 140522512643840
9 140522512643840
问题:
如何防止为thread_run 函数切换“上下文”?
如何使某些功能“原子化”?
预期结果(保留多个线程):
0 140522512643840
...
9 140522512643840
0 140522504251136
...
9 140522504251136
PS:需要保留调用thread_run(loop.run_in_executor)的方法。这只是一个简化的例子。我只询问案例,这在示例中进行了描述。我知道,有很多方法可以重构代码并摆脱loop.run_in_executor,但我试图在这种特殊情况下找到解决方案。
PSS:在 Windows 10 中的行为相同(循环增加到 100 个)
67 8704
16 14712
68 8704
69 8704
70 8704
17 14712
71 8704
更新:#1 我正在尝试使用装饰器:(来自this answer)
def synchronized(wrapped):
lock = threading.Lock()
@functools.wraps(wrapped)
def _wrap(*args, **kwargs):
with lock:
result = wrapped(*args, **kwargs)
return result
但它不适用于几个功能:
import threading
import asyncio
import functools
from concurrent.futures import ThreadPoolExecutor
executor = ThreadPoolExecutor(3)
def synchronized(wrapped):
lock = threading.Lock()
@functools.wraps(wrapped)
def _wrap(*args, **kwargs):
with lock:
result = wrapped(*args, **kwargs)
return result
return _wrap
@synchronized
def thread_run():
i = 0
for x in range(5):
i += 1
print(x, "thread_run", threading.get_ident())
@synchronized
def thread_run2():
i = 0
for x in range(5):
i += 1
print(x, "thread_run2", threading.get_ident())
def not_important():
i = 0
for x in range(5):
i += 1
print(x, "not_important", threading.get_ident())
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_in_executor(executor, thread_run)
loop.run_in_executor(executor, thread_run2)
loop.run_in_executor(executor, not_important)
loop.run_forever()
输出:
0 thread_run 140039310980864
0 thread_run2 140039302588160
0 not_important 140039220623104
1 not_important 140039220623104
2 not_important 140039220623104
1 thread_run2 140039302588160
2 thread_run2 140039302588160
3 thread_run2 140039302588160
4 thread_run2 140039302588160
3 not_important 140039220623104
4 not_important 140039220623104
1 thread_run 140039310980864
2 thread_run 140039310980864
3 thread_run 140039310980864
4 thread_run 140039310980864
预期:
每个函数(not_important 除外)按顺序运行。不是并行的。
更新 #2: 我用“半”解决方案添加了答案。但这并不能解决问题,当你想“makr”一个函数时,它不应该被任何其他函数打断。
【问题讨论】:
-
这种装饰器方法只会因为装饰器创建锁而中断,因此您最终会为每个装饰函数获得一个锁,它们可以独占获取。为了同步功能,可能只有一个锁。如果您将
lock = threading.Lock()从装饰器移动到__name__ == '__main__'块的开头,它应该会按照您的预期进行。
标签: python python-3.x multithreading python-asyncio