首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何处理tokio::产卵关闭需要‘静态和和自我?

如何处理tokio::产卵关闭需要‘静态和和自我?
EN

Stack Overflow用户
提问于 2021-11-13 14:46:45
回答 2查看 2.9K关注 0票数 3

我很难理解如何编写封装在一个单一结构中的并发异步代码。

我不知道如何准确地解释这个问题,所以我试着用一个例子来解释。

假设我有一个UdpServer结构。此结构有多个与其行为相关的方法(例如,handle_datagramdeserialize_datagram等)

如果我想使代码并发,我将生成tokio任务,它要求提供给它的闭包是静态的,这意味着我不能从这个任务中调用&self,只要&self不是静态的,这意味着我不能调用self.serialize_datagram()

我理解这个问题(不能保证结构会超过线程),但是找不到解决它的正确方法。我知道将函数移出impl是可能的,但在我看来,这并不是一个很好的解决方案。

此外,即使我们假设有一段时间我可以将&self作为静态代码,但由于某些原因(我想还不够生硬),这段代码在我看来仍然不太正确。

另一个“解决方案”是采用self: Arc<Self>而不是&self,但这感觉更糟。

所以我假设有一些模式我不知道。有人能解释一下我该怎么重构整件事吗?

示例代码:

代码语言:javascript
复制
struct UdpServer {}
impl UdpServer {
    pub async fn run(&self) {
        let socket = UdpSocket::bind(self.addr).await.unwrap();
        loop {
            let mut buf: &mut [u8] = &mut [];
            let (_, _) = socket.recv_from(&mut buf).await.unwrap();

            // I spawn tokio task to enable concurrency
            tokio::spawn(async move {
                // But i can't use &self in here because it's not static.
                let datagram = self.deserialize_datagram(buf).await;
                self.handle_datagram(()).await;
            });
        }
    }

    pub async fn deserialize_datagram(&self, buf: &mut [u8]) -> Datagram {
        unimplemented!()
    }

    pub async fn handle_datagram(&self, datagram: Datagram) {
        unimplemented!()
    }
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-11-14 14:23:18

目前,唯一的方法是通过使用self使Arc最后任意长。因为run()UdpServer上的一个方法,所以需要对Arc<Self>进行更改,您曾经考虑过这个更改,但由于感觉更糟而拒绝了。不过,这就是做这件事的方法:

代码语言:javascript
复制
pub async fn run(self: Arc<Self>) {
    let socket = UdpSocket::bind(&self.addr).await.unwrap();
    loop {
        let mut buf: &mut [u8] = &mut [];
        let (_, _) = socket.recv_from(&mut buf).await.unwrap();

        tokio::spawn({
            let me = Arc::clone(&self);
            async move {
                let datagram = me.deserialize_datagram(buf).await;
                me.handle_datagram(datagram).await;
            }
        });
    }
}

游乐场

有趣的是,smol异步运行时可能实际上提供了您想要的东西,因为它的执行器具有终生的功能。该生存期与调用方环境中的值相关联,执行器上产生的未来值可能会引用它。例如,它编译:

代码语言:javascript
复制
use futures_lite::future;
use smol::{Executor, net::UdpSocket};

struct Datagram;

struct UdpServer {
    addr: String,
}

impl UdpServer {
    pub async fn run<'a>(&'a self, ex: &Executor<'a>) {
        let socket = UdpSocket::bind(&self.addr).await.unwrap();
        loop {
            let mut buf: &mut [u8] = &mut [];
            let (_, _) = socket.recv_from(&mut buf).await.unwrap();

            ex.spawn({
                async move {
                    let datagram = self.deserialize_datagram(buf).await;
                    self.handle_datagram(datagram).await;
                }
            }).detach();
        }
    }

    pub async fn deserialize_datagram(&self, _buf: &mut [u8]) -> Datagram {
        unimplemented!()
    }

    pub async fn handle_datagram(&self, _datagram: Datagram) {
        unimplemented!()
    }
}

fn main() {
    let server = UdpServer { addr: "127.0.0.1:8080".to_string() };
    let ex = Executor::new();
    future::block_on(server.run(&ex));
}
票数 4
EN

Stack Overflow用户

发布于 2022-03-11 10:14:00

你说的完全正确。在东京教程中,提到了这个解决方案:

如果必须同时从多个任务访问单个数据,则必须使用同步原语(如弧形)共享数据。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69955340

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档