From b356524a9eb1b39aa6cae318f2ac14dc9bce3405 Mon Sep 17 00:00:00 2001 From: snowy Date: Sat, 17 Aug 2024 00:18:06 +0800 Subject: [PATCH] =?UTF-8?q?:memo:=20=E6=B7=BB=E5=8A=A0shared=5Fmemory?= =?UTF-8?q?=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/dev/dev_comm.md | 28 +++++++++++++++++++++++++--- docs/dev/dev_shared_memory.md | 24 ++++++++++++++++++++++++ liteyuki/comm/__init__.py | 11 ++++------- liteyuki/comm/storage.py | 31 +++++++++++++++++-------------- src/utils/base/__init__.py | 1 - 5 files changed, 70 insertions(+), 25 deletions(-) create mode 100644 docs/dev/dev_shared_memory.md diff --git a/docs/dev/dev_comm.md b/docs/dev/dev_comm.md index a6c2c7ed..3d898393 100644 --- a/docs/dev/dev_comm.md +++ b/docs/dev/dev_comm.md @@ -5,13 +5,15 @@ order: 4 category: 开发 --- -## 简介 +## **通道通信** + +### 简介 轻雪运行在主进程 MainProcess 里,其他插件框架进程是伴随的子进程,因此无法通过内存共享和直接对象传递的方式进行通信,轻雪提供了一个通道`Channel`用于跨进程通信,你可以通过`Channel`发送消息给其他进程,也可以监听其他进程的消息。 例如子进程接收到用户信息需要重启机器人,这时可以通过通道对主进程发送消息,主进程接收到消息后重启对应子进程。 -## 快速开始 +### 快速开始 通道是全双工的,有两种接收模式,但一个通道只能使用一种,即被动模式和主动模式,被动模式由`chan.on_receive()`装饰回调函数实现,主动模式需调用`chan.receive()`实现 @@ -77,4 +79,24 @@ async def on_startup(): ..-..-.. ..:..:.. [ℹ️信息] Passive receive: I am liteyuki main process passive ..-..-.. ..:..:.. [ℹ️信息] Active receive: I am liteyuki main process active ... -``` \ No newline at end of file +``` + +## **共享内存通信** + +### 简介 + +- 相比于普通进程通信,内存共享使得代码编写更加简洁,轻雪框架提供了一个内存共享通信的接口,你可以通过`storage`模块实现内存共享通信 +- 内存共享是线程安全的,你可以在多个线程中读写共享内存,线程锁会自动保护共享内存的读写操作 + +### 快速开始 + +- 在任意进程中均可使用 + +```python +from liteyuki.comm.storage import shared_memory + +shared_memory.set("key", "value") # 设置共享内存 +value = shared_memory.get("key") # 获取共享内存 +``` + +- 源代码:[liteyuki/comm/storage.py](https://github.com/LiteyukiStudio/LiteyukiBot/blob/main/liteyuki/comm/storage.py) \ No newline at end of file diff --git a/docs/dev/dev_shared_memory.md b/docs/dev/dev_shared_memory.md new file mode 100644 index 00000000..8c0473d6 --- /dev/null +++ b/docs/dev/dev_shared_memory.md @@ -0,0 +1,24 @@ +--- +title: 内存共享通信 +icon: exchange-alt +order: 5 +category: 开发 +--- + +# 简介 + +相比于普通进程通信,内存共享使得代码编写更加简洁,轻雪框架提供了一个内存共享通信的接口,你可以通过`shared_memory`模块实现内存共享通信 +内存共享是线程安全的,你可以在多个线程中读写共享内存,线程锁会自动保护共享内存的读写操作 + +## 快速开始 + +- 在任意进程中均可使用 + +```python +from liteyuki.comm.storage import shared_memory + +shared_memory.set("key", "value") # 设置共享内存 +value = shared_memory.get("key") # 获取共享内存 +``` + +- 源代码:[liteyuki/comm/storage.py](https://github.com/LiteyukiStudio/LiteyukiBot/blob/main/liteyuki/comm/storage.py) \ No newline at end of file diff --git a/liteyuki/comm/__init__.py b/liteyuki/comm/__init__.py index c6efd16b..6dac6959 100644 --- a/liteyuki/comm/__init__.py +++ b/liteyuki/comm/__init__.py @@ -1,13 +1,10 @@ # -*- coding: utf-8 -*- """ -Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved - -@Time : 2024/7/26 下午10:36 -@Author : snowykami -@Email : snowykami@outlook.com -@File : __init__.py -@Software: PyCharm 该模块用于轻雪主进程和Nonebot子进程之间的通信 +依赖关系 +event -> _ +storage -> channel +rpc -> channel, storage """ from liteyuki.comm.channel import ( Channel, diff --git a/liteyuki/comm/storage.py b/liteyuki/comm/storage.py index d4fa5ee3..b01b2083 100644 --- a/liteyuki/comm/storage.py +++ b/liteyuki/comm/storage.py @@ -136,8 +136,9 @@ class KeyValueStoreNoLock: if IS_MAIN_PROCESS: return self._store.get(key, default) else: - self.passive_chan.send(("get", key, default)) - return self.active_chan.receive() + temp_chan = Channel("temp_chan") + self.passive_chan.send(("get", key, default, temp_chan)) + return temp_chan.receive() def delete(self, key: str, ignore_key_error: bool = True) -> None: """ @@ -169,8 +170,9 @@ class KeyValueStoreNoLock: if IS_MAIN_PROCESS: return self._store else: - self.passive_chan.send(("get_all",)) - return self.active_chan.receive() + temp_chan = Channel("temp_chan") + self.passive_chan.send(("get_all", temp_chan)) + return temp_chan.receive() class GlobalKeyValueStore: @@ -197,28 +199,29 @@ if IS_MAIN_PROCESS: @shared_memory.passive_chan.on_receive(lambda d: d[0] == "get") - def on_get(d): - shared_memory.active_chan.send(shared_memory.get(d[1], d[2])) + def on_get(data: tuple[str, str, any, Channel]): + data[3].send(shared_memory.get(data[1], data[2])) @shared_memory.passive_chan.on_receive(lambda d: d[0] == "set") - def on_set(d): - shared_memory.set(d[1], d[2]) + def on_set(data: tuple[str, str, any]): + shared_memory.set(data[1], data[2]) @shared_memory.passive_chan.on_receive(lambda d: d[0] == "delete") - def on_delete(d): - shared_memory.delete(d[1]) + def on_delete(data: tuple[str, str]): + shared_memory.delete(data[1]) @shared_memory.passive_chan.on_receive(lambda d: d[0] == "get_all") - def on_get_all(d): - if d[0] == "get_all": - shared_memory.active_chan.send(shared_memory.get_all()) + def on_get_all(data: tuple[str, Channel]): + if data[0] == "get_all": + data[1].send(shared_memory.get_all()) else: + # 子进程在入口函数中对shared_memory进行初始化 shared_memory = None -_ref_count = 0 # 引用计数 +_ref_count = 0 # import 引用计数, 防止获取空指针 if not IS_MAIN_PROCESS: if (shared_memory is None) and _ref_count > 1: raise RuntimeError("Shared memory not initialized.") diff --git a/src/utils/base/__init__.py b/src/utils/base/__init__.py index 6f15eca5..a0b0a580 100644 --- a/src/utils/base/__init__.py +++ b/src/utils/base/__init__.py @@ -1,6 +1,5 @@ import threading -from nonebot import logger from liteyuki.comm.channel import active_channel