#run-time #thread #async #synchronization #unsafe #executor #async-io

无std unsend

专为线程不安全人员设计的线程不安全运行时

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 异步

Download history 18/week @ 2024-03-15 2/week @ 2024-03-22 15/week @ 2024-03-29 5/week @ 2024-04-05 18/week @ 2024-04-12 3/week @ 2024-04-19 30/week @ 2024-05-10 34/week @ 2024-05-17 54/week @ 2024-05-24 65/week @ 2024-05-31 22/week @ 2024-06-07 20/week @ 2024-06-14 27/week @ 2024-06-21 9/week @ 2024-06-28

83 每月下载量
2 crates 中使用

LGPL-3.0-or-later OR MPL-2.0

105KB
2K SLoC

unsend

专为线程不安全人员设计的线程不安全运行时。

大多数现代 async 运行时都是线程安全的,因为它们旨在用于需要多线程的联网应用程序。这种硬件并行性提高了并行程序的性能。然而,您可能希望避免这种同步。原因包括

  • 您正在处理的数据是 !Send,因此不能在线程之间共享。
  • 您想避免包含标准库或操作系统。
  • 您正在运行的嵌入式硬件不支持多线程。
  • 您想避免同步的开销,对于不那么并行的程序来说,同步可能弊大于利。例如,如果您的进程依赖于高度修改的共享数据结构,同步可能弊大于利。

像NodeJS和Redis这样的应用程序就利用了线程不安全性。在这些情况下,使用 unsend 并避免同步问题可能是有益的。

unsend 提供以下工具

  • 类似于 MutexRwLock 的同步原语等价物,但没有 Sync 并且没有同步代码。
  • 一个多生产者多消费者无界通道。
  • 一个单线程执行器。

注意事项

此crate中的大多数类型,例如同步原语和通道,都不涉及任何同步原语。没有原子操作、互斥锁或其他进程感知的内容。

然而,使用执行器,这变得显著更复杂。代码Waker需要是Send + Sync,这意味着内部调度函数必须是线程安全的。默认情况下,执行器使用线程感知的原子通道来存储任务。但是,如果启用了std功能,则Waker可以检测它是否是从创建它的同一个线程唤醒的。如果是这种情况,执行器将使用线程不安全的通道。

哲学

  • 使用轻量级任务而不是重量级线程。
  • 通过线程不安全的通道而不是线程安全的通道共享数据。
  • 简单胜于复杂。

特性

所有特性默认启用。

  • alloc启用在alloc存储库中使用全局分配器,从而启用通道的使用。
  • executor引入了更多的依赖关系,并启用了Executor类型。需要alloc
  • std启用使用标准库,从而启用某些优化。

范围之外

与其他async运行时不同,unsend故意避免提供某些流行的功能。其中一些功能包括

  • 未来和流组合器。代码futures-litefutures存储库提供了我们无法提供的许多功能,并且大多数它们默认与线程不安全类型一起工作。
  • 线程池。由于我们大多数情况下避免同步,因此线程池与这个存储库的哲学相悖。代码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