0

我实现了一个玩具 UDP 服务器,它通过多个并发任务尽可能快地通过一组套接字喷出数据。我天真地认为这可以通过利用 Tokio 的线程运行时来有效利用 CPU 资源:

use std::error::Error;
use std::io;
use tokio::net::UdpSocket;

struct Server {
    socket: UdpSocket,
    buf: Vec<u8>,
    id: u32,
}

const T_LOOPS: usize = 100000;

impl Server {
    async fn run(self) -> Result<(), io::Error> {
        let Server {
            mut socket,
            buf,
            id,
        } = self;

        let peer = "127.0.0.1:9876".to_string();

        loop {
            for _n in 0..T_LOOPS {
                let _amt = socket.send_to(&buf[..], &peer).await?;
            }

            println!("server {} run {} loops", id, T_LOOPS);
        }
    }
}

#[tokio::main(max_threads=8)]
async fn main() -> Result<(), Box<dyn Error>> {
    let addr = "0.0.0.0:0".to_string();

    for n in 0u32..4u32 {
        let socket = UdpSocket::bind(&addr).await?;

        let server = Server {
            socket: socket,
            buf: vec![0; 1500],
            id: n,
        };

        tokio::spawn(async move {
            server.run().await
        });
    }

    Ok(())
}

它实践,哪些任务运行似乎是相当不确定的。通常第一个生成的任务会运行,其他 3 个任务中的任意数量都会运行(并不总是按顺序运行)。例如,输出可能类似于:

server 0 run 100000 loops
server 0 run 100000 loops
server 0 run 100000 loops

或者

server 1 run 100000 loops
server 0 run 100000 loops
server 2 run 100000 loops
server 1 run 100000 loops
server 2 run 100000 loops
server 0 run 100000 loops

但并不是我所需要的(这将显示所有 4 个 ID)。

我不禁觉得我一定在这里遗漏了一些东西。我玩过core-threadsmax-threads没有任何有用的结果。

如何使这种类型的系统与 Tokio(或一般异步)可靠地工作?

虽然这是一个玩具系统,但它实际上是我想要的用例的垫脚石。

4

0 回答 0