线程与异步问题

Threading with asyncio issue

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

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

所以,问题是:

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

这里是代码,不要担心异常,线程连接等问题。作为示例进行了简化。

预期输出:

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))

您的每个线程都有自己的 data 实例。你用 d = data(id) 得到它。当您检查 d.ad.b 时看到该行为的原因是 它们 在所有线程之间共享。这与线程或异步无关;这是您定义 class.

的方式

当您将可变对象分配给 class 级属性时,这些对象将在 class.

的所有实例之间共享
>>> 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 = []

您甚至可以从 class 的定义中删除 id = 0,因为您在 __init__.

中分配了一个值
class data:
    def __init__(self, id):
        self.id = id
        self.a = []
        self.b = []

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

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] 来代替。