4个版本 (2个重大更改)
0.2.1 | 2023年5月28日 |
---|---|
0.2.0 | 2023年5月16日 |
0.1.0 | 2023年4月27日 |
0.0.1 | 2023年4月19日 |
#1006 in 异步
83 每月下载量
在 2 crates 中使用
105KB
2K SLoC
unsend
专为线程不安全人员设计的线程不安全运行时。
大多数现代 async
运行时都是线程安全的,因为它们旨在用于需要多线程的联网应用程序。这种硬件并行性提高了并行程序的性能。然而,您可能希望避免这种同步。原因包括
- 您正在处理的数据是
!Send
,因此不能在线程之间共享。 - 您想避免包含标准库或操作系统。
- 您正在运行的嵌入式硬件不支持多线程。
- 您想避免同步的开销,对于不那么并行的程序来说,同步可能弊大于利。例如,如果您的进程依赖于高度修改的共享数据结构,同步可能弊大于利。
像NodeJS和Redis这样的应用程序就利用了线程不安全性。在这些情况下,使用 unsend
并避免同步问题可能是有益的。
unsend
提供以下工具
- 类似于
Mutex
和RwLock
的同步原语等价物,但没有Sync
并且没有同步代码。 - 一个多生产者多消费者无界通道。
- 一个单线程执行器。
注意事项
此crate中的大多数类型,例如同步原语和通道,都不涉及任何同步原语。没有原子操作、互斥锁或其他进程感知的内容。
然而,使用执行器,这变得显著更复杂。代码Waker
需要是Send + Sync
,这意味着内部调度函数必须是线程安全的。默认情况下,执行器使用线程感知的原子通道来存储任务。但是,如果启用了std
功能,则Waker
可以检测它是否是从创建它的同一个线程唤醒的。如果是这种情况,执行器将使用线程不安全的通道。
哲学
- 使用轻量级任务而不是重量级线程。
- 通过线程不安全的通道而不是线程安全的通道共享数据。
- 简单胜于复杂。
特性
所有特性默认启用。
alloc
启用在alloc
存储库中使用全局分配器,从而启用通道的使用。executor
引入了更多的依赖关系,并启用了Executor
类型。需要alloc
。std
启用使用标准库,从而启用某些优化。
范围之外
与其他async
运行时不同,unsend
故意避免提供某些流行的功能。其中一些功能包括
- 未来和流组合器。代码
futures-lite
和futures
存储库提供了我们无法提供的许多功能,并且大多数它们默认与线程不安全类型一起工作。 - 线程池。由于我们大多数情况下避免同步,因此线程池与这个存储库的哲学相悖。代码
blocking
存储库提供了一个良好的async
感知线程池。 - 一个I/O反应堆。代码
async-io
提供了一个相当不错的、几乎适用于所有场合的最小反应堆。代码tokio
存储库也提供了一个反应堆。然而,在未来提供这可能是一个好主意,因为代码async-io
反应堆涉及大量的同步。如果您想看到这个功能,请提交一个PR。
最低支持的Rust版本
此存储库的最低支持Rust版本(MSRV)是1.48。作为一个试探性政策,MSRV不会超过Debian Stable提供的当前Rust版本。在编写本文时,这个Rust版本是1.48。然而,在生态系统的重大转变或安全漏洞的情况下,MSRV可能会进一步提高。
示例
使用代码unsend、代码blocking和代码async-io的基本TCP服务器。
use async_io::Async;
use blocking::{unblock, Unblock};
use futures_lite::prelude::*;
use std::cell::Cell;
use std::fs::File;
use std::net::TcpListener;
use unsend::channel::channel;
use unsend::executor::Executor;
let (tx, rx) = channel();
// A shared value that will be mutated by the tasks.
let shared = Cell::new(1);
// Spawn a task that will read from the channel and write to a log file.
let executor = Executor::new();
executor
.spawn(async move {
let file = unblock(|| File::create("log.txt")).await.unwrap();
let mut file = Unblock::new(file);
while let Ok(msg) = rx.recv().await {
let message = format!("Sent out: {}", msg);
file.write_all(message.as_bytes()).await.unwrap();
}
})
.detach();
executor
.run(async {
loop {
// Listen for incoming connections.
let listener = Async::<TcpListener>::bind(([0, 0, 0, 0], 3000)).unwrap();
// Accept a new connection.
let (mut stream, _) = listener.accept().await.unwrap();
// Spawn a task that will operate on the stream.
let tx = tx.clone();
let shared = &shared;
executor
.spawn(async move {
// Read a 4-byte big-endian integer from the stream.
let mut buf = [0; 4];
stream.read_exact(&mut buf).await.unwrap();
let value = u32::from_be_bytes(buf);
// Multiply it by the shared value.
let value = value * shared.get();
// Increment the shared value.
shared.set(shared.get() + 1);
// Write the value to the stream.
stream.write_all(&value.to_be_bytes()).await.unwrap();
// Send the value to be logged.
tx.send(value).unwrap();
})
.detach();
}
})
.await;
致谢
此存储库的部分基于Stjepan Glavina的代码smol。
许可证
unsend
是免费软件:您可以在以下任一许可条款下重新分发和/或修改它
- 由自由软件基金会发布的GNU Lesser General Public License的版本3,或者(根据您的选择)该许可证的任何后续版本。
- 由Mozilla基金会发布的Mozilla Public License版本2。
- 本项目的赞助者赞助者和贡献者贡献者可以使用赞助者许可,忽略GNU AGPL的共享条款。
unsend
分发的目的是希望它能有用,但没有任何保证;甚至没有关于其商销性或针对特定用途的适用性的暗示性保证。有关详细信息,请参阅GNU Lesser General Public License或Mozilla Public License。
您应已随unsend
收到GNU Lesser General Public License和Mozilla Public License的副本。如果没有,请参阅https://www.gnu.org/licenses/或https://www.mozilla.org/en-US/MPL/2.0/。
依赖项
~170KB