为什么`tokio :: main`报告错误“处理时检测到循环”?

时间:2019-12-11 06:56:19

标签: rust async-await udp rust-tokio

我正在使用Tokio和async / .await创建一个UDP服务器,可以在其中以异步方式接收和发送数据。

我的UDP套接字的SendHalf在多个任务之间共享。为此,我正在使用Arc<Mutex<SendHalf>>。这就是Arc<Mutex<_>>存在的原因。

use tokio::net::UdpSocket;
use tokio::net::udp::SendHalf;
use tokio::sync::mpsc;
use std::sync::{Arc, Mutex};
use std::net::SocketAddr;

struct Packet {
    sender: Arc<Mutex<SendHalf>>,
    buf: [u8; 512],
    addr: SocketAddr,
}

#[tokio::main]
async fn main() {
    let server = UdpSocket::bind(("0.0.0.0", 44667)).await.unwrap();
    let (mut server_rx, mut server_tx) = server.split();
    let sender = Arc::new(Mutex::new(server_tx));
    let (mut tx, mut rx) = mpsc::channel(100);

    tokio::spawn(async move {
        loop {
            let mut buffer = [0; 512];
            let (_, src) = server_rx.recv_from(&mut buffer).await.unwrap();
            let packet = Packet {
                sender: sender.clone(),
                buf: buffer,
                addr: src,
            };
            tx.send(packet).await;
        }
    });

    while let Some(packet) = rx.recv().await {
        tokio::spawn(async move {
            let mut socket = packet.sender.lock().unwrap();
            socket.send_to(&packet.buf, &packet.addr).await.unwrap();
        });
    }
}

这里也是Playground

我遇到了我不理解的编译器错误:

error[E0391]: cycle detected when processing `main`
  --> src/main.rs:13:1
   |
13 | #[tokio::main]
   | ^^^^^^^^^^^^^^
   |
note: ...which requires processing `main::{{closure}}#0::{{closure}}#1`...
  --> src/main.rs:34:33
   |
34 |           tokio::spawn(async move {
   |  _________________________________^
35 | |             let mut socket = packet.sender.lock().unwrap();
36 | |             socket.send_to(&packet.buf, &packet.addr).await.unwrap();
37 | |         });
   | |_________^
   = note: ...which again requires processing `main`, completing the cycle
note: cycle used when processing `main::{{closure}}#0`
  --> src/main.rs:13:1
   |
13 | #[tokio::main]
   | ^^^^^^^^^^^^^^

为什么我的代码产生一个循环?为什么呼叫需要处理main

该错误的详细含义是什么?我想了解发生了什么事。

2 个答案:

答案 0 :(得分:3)

根据tokio文档,关于using !Send value from a task

  

在对!Send的调用中保持.await的值将导致不友好的编译错误消息,类似于:

     

[... some type ...] cannot be sent between threads safely

     

或:

     

error[E0391]: cycle detected when processing main

您正在目睹此确切错误。当您lock a Mutex时:

pub fn lock(&self) -> LockResult<MutexGuard<T>>

它返回一个MutexGuard,即!Send

impl<'_, T: ?Sized> !Send for MutexGuard<'_, T>

这样可以编译:

#[tokio::main]
async fn main() {
    ...

    while let Some(packet) = rx.recv().await {
        let mut socket = packet.sender.lock().unwrap();
        socket.send_to(&packet.buf, &packet.addr).await.unwrap();
    }
}

答案 1 :(得分:-1)

我只是遇到了一个非常相似的问题(我使用的是RwLockMutex,但是结构几乎完全相同)。我想异步处理每个UDP数据包,而不是像读取此处的其他解决方案那样等待读取另一个数据包之前进行处理。

我为解决该问题所做的更改是使用tokio::sync::*同步结构,而不是std中的那些。因此,在您的情况下,将std::sync::Mutex切换到tokio::sync::Mutex(然后将.unwrap()切换到.await),就可以了。 (Documentation

我对您的游乐场进行了更改,现在正在编译: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=2b6114a12a5f784a38a595bc521f6f02

(非常感谢Tokio Slack在我遇到相同问题时向我提供了答案。只需将其传递出去即可。)