Skip to content
On this page

异步画

DANGER

知道拼积木吗?现在这一章差不多可以认为是零件乱放。

在看了那么多篇文档以及 其他 bot 的源码, 想必你已经做了一个带有 Pillow / PIL 的制图的模组吧~

但是,你有没有发现,随着你制图功能被调用的越来越多,你的 bot 又双叒叕卡了。

可能会吓得你赶紧去寻找问题出现的原因,随着你不断的收集数据及测试, 你最终会发现,原来是你的制图功能用时太久了。 太久也就算了,关键是在制图期间,任何其他代码都在等待

WARNING

以下办法通常情况下并不能帮你解决制图慢的问题,
只是将这个办法从同步变成了异步(即治标不治本)。

假设你真的想要加快制图的速度, 并且愿意牺牲一点点撸码体验,
那么建议你去试试 opencv-python 之类的库。
opencv-python 的使用方法虽然一点也不 Pythonic,但就速度与内存占用而言比 Pillow 猛多了

快速实例

python
from io import BytesIO

# 切记,PIL 的 Image 跟 ariadne 的 Image Element 名字重了
from PIL import Image as IMG


def make_pic(size = (100, 100), color = (255, 0, 0)):
    img = IMG.new("RGB", size, color)
    img.save(b := BytesIO(), "JPEG")  # 注意,此处使用了海象运算符
    return b.getvalue()


@channel.use([GroupMessage])
async def drawing(group: Group):
    pic = make_pic()
    await app.send_message(group, MessageChain(Image(pic)))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
python
from io import BytesIO

# 切记,PIL 的 Image 跟 ariadne 的 Image Element 名字重了
from PIL import Image as IMG
from graia.ariadne.util.async_exec import io_bound, cpu_bound


@io_bound
def make_pic(size = (100, 100), color = (255, 0, 0)):
    img = IMG.new("RGB", size, color)
    img.save(b := BytesIO(), "JPEG")  # 注意,此处使用了海象运算符
    return b.getvalue()


@channel.use([GroupMessage])
async def drawing(group: Group):
    pic = await make_pic()
    await app.send_message(group, MessageChain(Image(pic)))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
python
import asyncio
from io import BytesIO

# 切记,PIL 的 Image 跟 ariadne 的 Image Element 名字重了
from PIL import Image as IMG


def make_pic(size = (100, 100), color = (255, 0, 0)):
    img = IMG.new("RGB", size, color)
    img.save(b := BytesIO(), "JPEG")  # 注意,此处使用了海象运算符
    return b.getvalue()


@channel.use([GroupMessage])
async def drawing(group: Group):
    pic = await asyncio.to_thread(make_pic())
    await app.send_message(group, MessageChain(Image(pic)))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

TIP

io_boundasyncio.to_thread 除了使用方法可能有所不同外,本质其实没有多大区别。 不过假设你使用的是 Python3.8,那就没有 asyncio.to_thread

这是什么?

GIL 是什么?

应该什么时候用?

先说结论

  • 如果你的函数造成的延迟你几乎感觉不到,那你就直接用
  • 如果你的函数是 io 密集型 或者是在运行途中可以 释放 GIL 锁,那你就用 io_bound
  • 如果你的函数是 cpu 密集型 且运行途中 不可以释放 GIL 锁,那你就用 cpu_bound

然后我们来仔细讲一讲

Moyuing~

Tip:

本文档使用 CC BY-NC-SA 4.0 协议进行共享,详情见 README

MIT License