3 个版本
0.1.2 | 2019年3月29日 |
---|---|
0.1.1 | 2019年3月25日 |
0.1.0 | 2019年3月25日 |
#1028 in 并发
每月24次下载
在 movie_example 中使用
26KB
电影
一个actor/线程编排库
概述
- 几乎没有样板代码 - 看示例
- 与稳定编译器兼容,但需要2018版
- 除了
std
外没有外部依赖 - 基于枚举的MPSC通道通信
- 默认情况下,一个actor = 一个线程
- 默认情况下,actor只接受消息,不发送回复
- 发送回复的解决方案现在还不是最优雅的,请参见下面的高级示例
- 网络RPC应该是可能的,但超出了此crate的范围。如果你想这样做,可以使用
input_derive
和custom_code
来派生Serialize
和Deserialize
。 - 两个过程宏 - 请参阅
movie_derive
- actor需要在模块/crate范围内定义
- 目前错误消息不好,使用宏加手动字符串解析魔法
- 如果在(稳定的)
TokenStream::to_string()
中有大的破坏性变化,宏可能会中断
示例
以下示例被测试为忽略的,因为据我所知,在文档测试中无法运行过程宏。它们也在 tests
目录中,在那里它们被测试。
安装
[dependencies]
"movie" = "0.1"
简单actor
use movie::actor;
actor! { SimplestActor } // completely useless
actor! {
SimpleActor
input: Ping,
on_message:
Ping => (),
}
#[test]
fn test_simple_actor() {
use SimpleActor::{Actor, Input};
// Create and spawn the actor
let actor = Actor {}.start();
actor.send(Input::Ping);
actor.stop(); // Will block, waiting for actor.
}
高级示例
use movie::actor;
use std::sync::mpsc::Sender;
actor! {
StreamParsingActor
public_visibility: true,
docs: /// Actor that parses video from V4L2 device
/// It's very consistent - failed every time so far.
input:
ChangeSource(String),
SendState,
// By default, Input enum does not have any trait auto-implemented.
input_derive: Debug, PartialEq,
// Whitespace and comments are irrelevant.
// It's also optional to end sections (attributes) with a comma, with
// exception of code attributes (on_stop, on_init etc.), which should
// not end with comma, but rather either with nothing or with a semicolon.
data:
pub device: String,
pub state_tx: Sender<u64>,
on_init:
if self.device == "admin secret device" {
panic!("No access right for admin secret device");
}
let mut lines_parsed = 0; // This variable will be exposed to on_message.
// This is suboptimal, but it is the simplest
// way to allow for thread-local variables (`data`
// is sent between threads, so it couldn't be used
// e.g. for GTK references)
on_message:
ChangeSource(name) => {
self.device = name;
},
SendState => {
self.state_tx.send(lines_parsed).unwrap();
}
tick_interval: 5, // Every 5ms, default = 100
on_tick: // on_message have priority over on_tick
lines_parsed += 1;
on_stop: ()
// custom_code must end with a semicolon
custom_code:
pub const DEFAULT_DEVICE: &'static str = "video0";
}
#[test]
fn test_stream_parsing_actor() {
use StreamParsingActor::{Actor, Input, DEFAULT_DEVICE};
use std::sync::mpsc::channel;
let (tx, rx) = channel();
let cfg = Actor {
device: DEFAULT_DEVICE.to_string(),
state_tx: tx,
};
// Spawn the actor, let on_init run
let actor = cfg.start(); // returns StreamParsingActor::Handle
use std::thread::sleep;
use std::time::Duration;
sleep(Duration::from_millis(100));
// We can use auto-derived traits on Input
actor.send(dbg!(Input::SendState));
println!("Ticked {} times in 100ms", rx.recv().unwrap()); // 20
actor.stop();
}
扩展示例
请参阅movie_example
。
actor属性
如果这些词后面跟着冒号,它们是受限关键字。
input
- 定义Input
枚举input_derive
- 为Input
枚举提供#[derive)]
data
- actor 状态变量,需要在创建actor时设置on_init
- 在演员开始接收消息之前运行on_message
- 定义match 消息
逻辑tick_interval
- 两次tick之间的时间(毫秒)。当未定义时,设置为100ms。影响消息轮询,因此不要设置得太高。on_tick
- 每次tick时运行on_stop
- 在演员停止接收消息之后运行spawner
- 创建线程的函数名称(默认为std::thread::spawn
,在此处放置一个具有相似签名的函数,以便将演员作为future、M:N线程等运行)spawner_return_type
-spawner
的返回类型(默认为std::thread::JoinHandle<()>
)custom_code
- 要插入到生成的演员模块中的代码public_visibility
- 如果true
,则演员模块是公开的docs
- 在此处放置文档 - 例如docs: /// 一个演员
某些代码可能会破坏宏的内部(例如,没有定义自己的循环就使用 break
或 continue
),这可能会破坏演员的主循环,放置 on_stop: (),
将导致无效逗号。调试它可能会很困难,希望 actor_dbg
(当代码无法编译时)和 cargo-expand
(当代码可以编译时)将有助于您在这些情况下。
历史
以前,我写过 x11-input-supercharger
,这是一个用于自动滚动和条件鼠标到键盘重绑定的实用程序(不改变键映射本身,这会导致基于Chromium的应用程序冻结一秒钟)。它运行着许多线程 - 一个用于自动滚动,其他用于重绑定,另一个用于轮询X11事件,还有一个用于显示带有类似Windows自动滚动状态指示器的GTK3窗口。代码并不复杂,消息层次结构也很简单(父消息传递给子消息,而不是相反),但样板代码的增加成为了代码可读性的很大障碍,尤其是在线程还需要执行自己的工作,而不仅仅是响应消息的情况下(工作包括与X11、GTK3交互以及跟踪时间)。而不是使用 actix
,我决定尝试实现自己的库,受到 actress
的启发,并花费了大约12个小时(我远非熟练的Rust程序员,这也是我第一次使用过程宏)。
这是我第一个“真正的”库(我之前的项目/Rust程序是CLI/GUI工具,有时带有简单的公共Rust API)。我发布了一些到crates.io,但不是全部(一些在GitHub上,一些尚未发布)。我了解到了过程宏(主要关于它们当前的不足)——遗憾的是,关于它们的资料并不多。我主要依赖于Rust参考手册。出人意料的是,我也无法快速找到有关文档的正确方法(带有链接和示例)——所以我使用了Rust书籍的第一版。我可能最终应该阅读第二版,以便熟悉里面的内容以及它们的位置(在我开始的时候,还没有第二版)。
野外的使用
如果你在项目中使用这个库,请考虑将其放在这里(如果可能的话)。这将帮助我测试我引入的新更改是否会破坏任何东西。
x11-input-supercharger
由我创建——参见自动滚动演员
常见问题解答
请参阅Reddit上的原始公告。
文档
README.md使用cargo-readme
构建(cargo readme > README.md
)。文档由docs.sh
脚本构建。
许可证
版权所有2019年Paweł Zmarzły。根据您的选择,许可方式为:
- Apache许可证2.0版本(
LICENSE-APACHE.md
或https://www.apache.org/licenses/LICENSE-2.0) - MIT许可证(
LICENSE-MIT.md
或https://opensource.org/licenses/MIT)
任意选择。
贡献
除非您明确声明,否则根据Apache-2.0许可证定义的,您有意提交以包含在作品中的任何贡献,都将按上述方式双许可,没有任何附加条款或条件。
许可证:MIT OR Apache-2.0