【问题标题】:How to prevent switching between threads during run of some function. Python如何防止在某些功能运行期间在线程之间切换。 Python
【发布时间】: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


【解决方案1】:

run_in_executor 返回一个未来,您可以等待归档顺序执行

import asyncio
import threading
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())

async def run(loop):
    for _ in range(2):
        await loop.run_in_executor(executor, thread_run)

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    asyncio.ensure_future(run(loop))
    loop.run_forever()

【讨论】:

  • 是的,但这只是“重构”的情况。正如我在PSS 部分中提到的:我不想重构任何东西。只是尝试在当前代码案例中找到解决方案。 + 我的例子只是简化了。有很多不同功能的线程,我只需要保护一个功能。
  • 主要问题是“如何防止函数切换上下文?”。您可以想象,然后您有 16 个以上的线程以及许多当前正在运行的功能。在这种情况下,我试图找到保护独立函数不受其他线程影响的方法。
【解决方案2】:

当前解决方案(未完全解决)基于评论@shmee

import threading
import asyncio
import functools
from concurrent.futures import ThreadPoolExecutor

executor = ThreadPoolExecutor(3)


def synchronized(wrapped):
    @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(10):
        i += 1
        print(x, "thread_run", threading.get_ident())


@synchronized
def thread_run2():
    i = 0
    for x in range(10):
        i += 1
        print(x, "thread_run2", threading.get_ident())


def not_important():
    i = 0
    for x in range(10):
        i += 1
        print(x, "not_important", threading.get_ident())


if __name__ == '__main__':
    lock = threading.Lock()  # global lock

    loop = asyncio.get_event_loop()
    for x in range(5):
        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 140192637171456
1 thread_run 140192637171456
2 thread_run 140192637171456
3 thread_run 140192637171456
4 thread_run 140192637171456
5 thread_run 140192637171456
6 thread_run 140192637171456
7 thread_run 140192637171456
8 thread_run 140192637171456
9 thread_run 140192637171456
0 thread_run2 140192637171456
1 thread_run2 140192637171456
2 thread_run2 140192637171456
3 thread_run2 140192637171456
4 thread_run2 140192637171456
5 thread_run2 140192637171456
6 thread_run2 140192637171456
7 thread_run2 140192637171456
8 thread_run2 140192637171456
9 thread_run2 140192637171456
0 not_important 140192620386048
1 not_important 140192620386048
2 not_important 140192620386048
3 not_important 140192620386048
0 thread_run 140192637171456
1 thread_run 140192637171456
2 thread_run 140192637171456
3 thread_run 140192637171456
4 thread_run 140192637171456
5 thread_run 140192637171456
6 thread_run 140192637171456
7 thread_run 140192637171456
8 thread_run 140192637171456
9 thread_run 140192637171456
0 thread_run2 140192637171456
1 thread_run2 140192637171456
2 thread_run2 140192637171456
3 thread_run2 140192637171456
4 thread_run2 140192637171456
4 not_important 140192620386048
5 not_important 140192620386048
6 not_important 140192620386048
7 not_important 140192620386048
8 not_important 140192620386048
9 not_important 140192620386048
0 not_important 140192620386048
1 not_important 140192620386048
2 not_important 140192620386048
3 not_important 140192620386048
4 not_important 140192620386048
5 not_important 140192620386048
6 not_important 140192620386048
7 not_important 140192620386048
8 not_important 140192620386048
9 not_important 140192620386048
5 thread_run2 140192637171456
6 thread_run2 140192637171456
7 thread_run2 140192637171456
8 thread_run2 140192637171456
9 thread_run2 140192637171456
0 not_important 140192637171456
1 not_important 140192637171456
2 not_important 140192637171456
3 not_important 140192637171456
4 not_important 140192637171456
5 not_important 140192637171456
0 thread_run 140192620386048
1 thread_run 140192620386048
6 not_important 140192637171456
7 not_important 140192637171456
8 not_important 140192637171456
9 not_important 140192637171456
2 thread_run 140192620386048
3 thread_run 140192620386048
4 thread_run 140192620386048
5 thread_run 140192620386048
6 thread_run 140192620386048
7 thread_run 140192620386048
8 thread_run 140192620386048
9 thread_run 140192620386048
0 thread_run2 140192628778752
1 thread_run2 140192628778752
2 thread_run2 140192628778752
3 thread_run2 140192628778752
4 thread_run2 140192628778752
5 thread_run2 140192628778752
6 thread_run2 140192628778752
7 thread_run2 140192628778752
8 thread_run2 140192628778752
9 thread_run2 140192628778752
0 thread_run 140192637171456
1 thread_run 140192637171456
2 thread_run 140192637171456
3 thread_run 140192637171456
4 thread_run 140192637171456
5 thread_run 140192637171456
6 thread_run 140192637171456
7 thread_run 140192637171456
0 not_important 140192628778752
1 not_important 140192628778752
2 not_important 140192628778752
3 not_important 140192628778752
4 not_important 140192628778752
5 not_important 140192628778752
6 not_important 140192628778752
7 not_important 140192628778752
8 not_important 140192628778752
9 not_important 140192628778752
8 thread_run 140192637171456
9 thread_run 140192637171456
0 thread_run2 140192620386048
1 thread_run2 140192620386048
2 thread_run2 140192620386048
3 thread_run2 140192620386048
4 thread_run2 140192620386048
5 thread_run2 140192620386048
6 thread_run2 140192620386048
7 thread_run2 140192620386048
8 thread_run2 140192620386048
9 thread_run2 140192620386048
0 thread_run 140192628778752
1 thread_run 140192628778752
2 thread_run 140192628778752
3 thread_run 140192628778752
4 thread_run 140192628778752
5 thread_run 140192628778752
6 thread_run 140192628778752
7 thread_run 140192628778752
8 thread_run 140192628778752
9 thread_run 140192628778752
0 not_important 140192637171456
1 not_important 140192637171456
2 not_important 140192637171456
3 not_important 140192637171456
4 not_important 140192637171456
5 not_important 140192637171456
0 thread_run2 140192620386048
1 thread_run2 140192620386048
2 thread_run2 140192620386048
3 thread_run2 140192620386048
4 thread_run2 140192620386048
5 thread_run2 140192620386048
6 thread_run2 140192620386048
7 thread_run2 140192620386048
8 thread_run2 140192620386048
9 thread_run2 140192620386048
6 not_important 140192637171456
7 not_important 140192637171456
8 not_important 140192637171456
9 not_important 140192637171456

当前结果
函数thread_runthread_run2 没有冲突。但它们可能会被not_important 函数打断。

所以这部分解决了我的问题。我想找到防止任何其他功能中断thread_runthread_run2 功能的方法。因为在实际项目中还有很多其他方法/功能,我无法为所有这些添加synchronized

【讨论】:

    猜你喜欢
    • 2016-09-10
    • 1970-01-01
    • 2015-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多