You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Tokio实现嵌套并发时遇“cannot recursively call into `Core`”错误求解

解决Tokio Timer与Hyper结合时的"cannot recursively call into Core" Panic

这个错误的核心原因是你在已经运行的Tokio Runtime中尝试创建并运行另一个Core实例——tokio::run本身已经启动了一个基于tokio-core的Runtime,而你在and_then回调里又调用了core.run(work).unwrap(),这就触发了递归调用Core的panic(Core的设计不允许嵌套运行)。

正确的做法:共享同一个Runtime Handle

你不需要在回调里新建Core,而是应该复用tokio::run提供的Runtime上下文,直接将HTTP请求的Future串联到Timer的Future链中,让整个任务在同一个Runtime里执行。

下面是修改后的完整代码:

修改后的main.rs

extern crate futures;
extern crate hyper;
extern crate tokio;
extern crate tokio_core;
extern crate tokio_timer;

use futures::{Future, Stream};
use hyper::Client;
use tokio_core::reactor::Handle;
use std::time::{Duration, Instant};
use tokio::timer::Delay;
use std::io::{self, Write};

fn main() {
    // 创建唯一的Core实例,所有异步任务都在这个Runtime中执行
    let mut core = Core::new().unwrap();
    let handle = core.handle();

    let when = Instant::now() + Duration::from_secs(1);
    
    let task = Delay::new(when)
        .map_err(|e| panic!("timer failed; err={:?}", e))
        .and_then(move |_| {
            // 使用已有的Runtime Handle创建Hyper Client
            let client = Client::new(&handle);
            let uri = "http://httpbin.org/ip".parse().unwrap();
            
            // 将HTTP请求作为Future链的一部分返回,无需单独启动新的事件循环
            client.get(uri).and_then(|res| {
                println!("Response: {}", res.status());
                res.body()
                    .for_each(|chunk| io::stdout().write_all(&chunk).map_err(From::from))
            })
            .map_err(|e| eprintln!("HTTP request failed: {:?}", e))
        });

    // 在同一个Core上运行整个任务链
    core.run(task).unwrap();
}

为什么这样能解决问题?

  • 我们只创建了一个Core实例,所有异步任务(Timer和HTTP请求)都在这个Runtime中调度执行,彻底避免了递归调用Core的问题。
  • Hyper的Client依赖于Runtime的Handle,直接复用同一个Handle就能让HTTP请求在现有Runtime中运行,不需要额外启动新的事件循环。

额外优化:实现周期性任务

如果你需要定期重复发起HTTP请求(而不是只执行一次),可以把Timer和HTTP请求包装成循环的Future:

// 新增周期性任务函数
fn periodic_task(handle: Handle) -> impl Future<Item = (), Error = ()> {
    let when = Instant::now() + Duration::from_secs(1);
    Delay::new(when)
        .map_err(|e| panic!("timer failed; err={:?}", e))
        .and_then(move |_| {
            let client = Client::new(&handle);
            let uri = "http://httpbin.org/ip".parse().unwrap();
            
            client.get(uri).and_then(|res| {
                println!("Response: {}", res.status());
                res.body()
                    .for_each(|chunk| io::stdout().write_all(&chunk).map_err(From::from))
            })
            .map_err(|e| eprintln!("HTTP request failed: {:?}", e))
            // 递归调用自身,实现周期性执行
            .and_then(move || periodic_task(handle.clone()))
        })
}

// 修改main中的任务执行逻辑
fn main() {
    let mut core = Core::new().unwrap();
    let handle = core.handle();

    let task = periodic_task(handle);
    core.run(task).unwrap();
}

这样你的服务就会每隔1秒发起一次HTTP请求,且不会触发之前的panic。

内容的提问来源于stack exchange,提问作者mchlstckl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 09:03:44