Rust中函数返回后出现50ms延迟的原因排查
Rust中play_oscillator返回Instant后出现额外延迟的原因
你遇到的延迟问题核心在于音频流相关变量的析构(Drop)时机,和Instant的记录位置无关,具体拆解如下:
关键原因:局部变量_stream与stream_handle的销毁耗时
在play_oscillator函数中,_stream和stream_handle是函数内的局部变量。当函数执行到return now这一步时,这两个变量还没有被销毁——函数的局部变量会在函数执行完毕、返回值被传递出去之后,才会触发析构逻辑。
而音频流的OutputStream和对应的stream_handle在析构(Drop)时,需要完成以下操作:
- 等待后台音频播放线程处理完剩余的缓冲数据
- 同步关闭音频设备的连接
- 释放相关的系统音频资源
这些操作会消耗额外的时间(也就是你看到的50ms),而这段时间刚好发生在play_oscillator返回now之后、main函数中计算elapsed之前,所以被算进了耗时里。
为什么移到main里就没延迟?
当你把Instant::now()移到main函数中play_oscillator调用的下一行时:
play_oscillator执行完毕后立刻返回,main函数马上调用Instant::now()记录时间- 此时
play_oscillator里的_stream和stream_handle还没开始析构(析构会在main后续代码执行过程中或者函数结束时发生) - 所以计算
elapsed时不会包含这段析构的耗时,结果就显示为0ms
你的代码参考
main函数代码
fn main() { notes.push(note_array[24]); let now = output::play_oscillator(&patch, ¬es, 1000); let elapsed = now.elapsed().as_millis(); println!("Elapsed: {:.2?}", elapsed); notes.clear(); }
play_oscillator函数代码
#[inline] pub fn play_oscillator(patch: &SynthPatch, notes: &Vec<Note>, duration: u64) -> Instant { let (_stream, stream_handle) = OutputStream::try_default().unwrap(); for i in 0..notes.len() { let mut oscillator = WavetableOscillator::new(patch.oscillator_type.sample_rate.clone(), patch.oscillator_type.wave_table.clone()); oscillator.set_frequency(notes[i].frequency); let oscillator = filter_processor::apply_filter(oscillator, &patch.filter); let oscillator = oscillator.convert_samples::<f32>(); let oscillator = stream_handle.play_raw(oscillator); let _result: &Result<(), PlayError> = &oscillator; } std::thread::sleep(std::time::Duration::from_millis(duration)); let now = Instant::now(); now }
解决建议
- 准确测量函数内部耗时:如果要统计
play_oscillator内部逻辑(包括sleep的1000ms)的真实耗时,应该在函数开头记录起始时间,结尾返回起始时间:
pub fn play_oscillator(patch: &SynthPatch, notes: &Vec<Note>, duration: u64) -> Instant { let start = Instant::now(); // 开头记录起始时间 // ... 原有逻辑 ... std::thread::sleep(std::time::Duration::from_millis(duration)); start // 返回起始时间 }
这样main里计算elapsed就只会包含函数内部的执行时间,不会被析构耗时干扰。
- 延长音频流生命周期:如果需要避免析构带来的延迟,可以把
_stream和stream_handle作为返回值传递到main函数中,让它们在main的后期再被销毁,这样就不会影响play_oscillator返回后的耗时测量。
内容的提问来源于stack exchange,提问作者willxw
相关产品推荐
相关产品推荐

