线程与异步问题

时间:2021-03-02 14:58:46

标签: python multithreading python-asyncio

我想运行几个线程,并且在每个线程中都有独立的异步循环,其中将处理异步例程列表。

每个线程都创建了类 'data' 的本地实例,但实际上它看起来像是线程之间的共享对象。我不明白为什么会这样。

所以,问题是:

  1. 为什么会这样?每个线程都应该有自己的本地“数据”实例(唯一)。
  2. 如何解决这个问题?不需要与对象“数据”进行跨线程同步。

这里是代码,不用担心异常、线程加入等问题。简化为示例。

预期输出:

id=1, list a: ['1', '1', '1']

实际输出:

id=1, list a: ['1', '3', '2', '1', '3', '2', '3', '2', '1']

数据处理:

class data:

id = 0
a = []
b = []

def __init__(self, id):
    self.id = id

async def load_0(self):
    for i in range(0, 3):
        self.a.append(str(self.id))
        await asyncio.sleep(0.1)

async def load_n(self):
    for i in range(0, 3):
        self.b.append(str(self.id))
        await asyncio.sleep(0.1)

在线程中运行异步任务:

async def thread_loop(loop, id):
    tasks = []

    d = data(id)

    # 0 .. n tasks
    tasks.append(asyncio.create_task(d.load_0()))
    tasks.append(asyncio.create_task(d.load_n()))

    await asyncio.gather(*tasks, return_exceptions = True)

if (id == 1):
    print('id=' + str(d.id) + ', list a: ' + str(d.a))

线程中的新事件循环:

def thread_main(id):
    loop = asyncio.new_event_loop()
    loop.run_until_complete(thread_loop(loop, id))

创建和启动线程:

async def start(threads):
    threads.append(threading.Thread(target = thread_main, args = (1,)))
    threads.append(threading.Thread(target = thread_main, args = (2,)))

    for thread in threads:
        thread.start()

    while True:
        await asyncio.sleep(0.1)

主要内容:

if __name__ == '__main__':
    threads = []
    loop = asyncio.get_event_loop()
    loop.run_until_complete(start(threads))

1 个答案:

答案 0 :(得分:2)

您的每个线程都有自己的 data 实例。您可以通过 d = data(id) 获得。您在检查 d.ad.b 时看到这种行为的原因是 它们 在所有线程之间共享。这与线程或 asyncio 无关;这是您定义类的方式。

当您将可变对象分配给类级属性时,这些对象在类的所有实例之间共享。

>>> class C:
...     l = []
...
>>> c1 = C()
>>> c2 = C()
>>>
>>> c1.l.append(1)
>>> c2.l
[1]

解决此问题的方法是将初始值的分配移至 __init__

>>> class C:
...     def __init__(self):
...         self.l = []
...
>>> c1 = C()
>>> c2 = C()
>>>
>>> c1.l.append(1)
>>> c2.l
[]

在你的情况下

class data:
    id = 0

    def __init__(self, id):
        self.id = id
        self.a = []
        self.b = []

您甚至可以从类的定义中删除 id = 0,因为您在 __init__ 中分配了一个值。

class data:
    def __init__(self, id):
        self.id = id
        self.a = []
        self.b = []

这可能超出您的需要,尤其是在不知道您的真实代码是什么样子的情况下,但您也可以考虑使用数据类。

from dataclasses import dataclass, field

@dataclass
class data:
    id: int
    a: list[str] = field(default_factory=list)
    b: list[str] = field(default_factory=list)

注意:使用 list[str] 需要 Python 3.10 或 from __future__ import annotations。否则,您将需要改用 typing.List[str]