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

Rust中无需Arc/Mutex实现多线程下载器的暂停功能

Rust下载器实现暂停功能的解决方案

你的核心问题是下载任务长时间持有锁,导致暂停请求无法及时修改状态。下面给出两种可行的实现方案,解决多任务共享状态下的暂停问题:

方案一:原子标记+异步Mutex拆分状态

这种方案用AtomicBool处理频繁检查的暂停标志,用异步Mutex管理其他状态和数据,避免长时间持有锁:

use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use tokio::time::{sleep, Duration};

#[derive(Debug, Clone, Copy)]
enum DownloadStatus {
    Idle,
    Downloading,
    Paused,
}

struct Downloader {
    status: tokio::sync::Mutex<DownloadStatus>,
    paused: Arc<AtomicBool>,
    // 存储下载数据,用异步Mutex保证线程安全
    downloaded_data: tokio::sync::Mutex<Vec<u8>>,
}

impl Downloader {
    pub fn new() -> Self {
        Self {
            status: tokio::sync::Mutex::new(DownloadStatus::Idle),
            paused: Arc::new(AtomicBool::new(false)),
            downloaded_data: tokio::sync::Mutex::new(Vec::new()),
        }
    }

    // 触发暂停:修改状态并设置原子标记
    pub async fn pause(&self) {
        *self.status.lock().await = DownloadStatus::Paused;
        self.paused.store(true, Ordering::SeqCst);
    }

    pub async fn download(&self) {
        // 更新为下载中状态,重置暂停标记
        *self.status.lock().await = DownloadStatus::Downloading;
        self.paused.store(false, Ordering::SeqCst);
        
        loop {
            // 无锁检查暂停标记,原子操作保证线程安全
            if self.paused.load(Ordering::SeqCst) {
                *self.status.lock().await = DownloadStatus::Paused;
                break;
            }

            // 模拟分块下载(实际场景替换为网络读取逻辑)
            sleep(Duration::from_millis(500)).await;
            println!("完成一块数据下载");

            // 短暂获取锁写入数据,用完自动释放
            let mut data_guard = self.downloaded_data.lock().await;
            data_guard.extend_from_slice(&[0x01, 0x02, 0x03, 0x04]);
        }

        println!("下载已暂停");
    }
}

#[tokio::main]
async fn main() {
    let downloader = Arc::new(Downloader::new());

    // 启动下载任务
    let download_task = tokio::spawn({
        let downloader = Arc::clone(&downloader);
        async move {
            downloader.download().await;
        }
    });

    // 启动暂停任务(模拟2秒后触发暂停)
    let pause_task = tokio::spawn({
        let downloader = Arc::clone(&downloader);
        async move {
            sleep(Duration::from_secs(2)).await;
            downloader.pause().await;
            println!("已触发暂停指令");
        }
    });

    // 等待两个任务完成
    let _ = tokio::try_join!(download_task, pause_task);
}

方案二:仅用异步Mutex(适合状态复杂场景)

如果你的下载状态不止简单的暂停/运行,需要更复杂的状态管理,可以全程用异步Mutex,但要保证下载循环中不长期持有锁:

use std::sync::Arc;
use tokio::sync::Mutex;
use tokio::time::{sleep, Duration};

#[derive(Debug, Clone, Copy)]
enum DownloadStatus {
    Idle,
    Downloading,
    Paused,
}

struct Downloader {
    status: DownloadStatus,
    downloaded_data: Vec<u8>,
}

impl Downloader {
    pub fn new() -> Self {
        Self {
            status: DownloadStatus::Idle,
            downloaded_data: Vec::new(),
        }
    }

    pub fn pause(&mut self) {
        self.status = DownloadStatus::Paused;
    }

    pub async fn download(&mut self) {
        self.status = DownloadStatus::Downloading;
        
        loop {
            // 每次循环前检查状态
            if self.status == DownloadStatus::Paused {
                break;
            }

            // 模拟分块下载
            sleep(Duration::from_millis(500)).await;
            println!("完成一块数据下载");
            self.downloaded_data.extend_from_slice(&[0x01, 0x02, 0x03, 0x04]);
        }

        println!("下载已暂停");
    }
}

#[tokio::main]
async fn main() {
    let downloader = Arc::new(Mutex::new(Downloader::new()));

    let download_task = tokio::spawn({
        let downloader = Arc::clone(&downloader);
        async move {
            loop {
                // 临时获取锁检查状态
                let mut guard = downloader.lock().await;
                if guard.status == DownloadStatus::Paused {
                    break;
                }
                drop(guard); // 提前释放锁,避免阻塞暂停任务

                // 模拟下载耗时
                sleep(Duration::from_millis(500)).await;
                println!("完成一块数据下载");

                // 重新获取锁写入数据
                let mut guard = downloader.lock().await;
                guard.downloaded_data.extend_from_slice(&[0x01, 0x02, 0x03, 0x04]);
            }
        }
    });

    let pause_task = tokio::spawn({
        let downloader = Arc::clone(&downloader);
        async move {
            sleep(Duration::from_secs(2)).await;
            let mut guard = downloader.lock().await;
            guard.pause();
            println!("已触发暂停指令");
        }
    });

    let _ = tokio::try_join!(download_task, pause_task);
}

关键注意事项

  1. 用异步Mutex替代标准库Mutex:tokio的tokio::sync::Mutex是异步的,不会阻塞线程,适合异步任务场景。
  2. 避免长期持有锁:下载任务不能在整个下载过程中一直持有锁,必须分块执行,每块完成后释放锁,让暂停任务有机会获取锁修改状态。
  3. 原子类型优化频繁检查:对于暂停这种需要频繁检查的标志,用AtomicBool这类原子类型可以避免频繁加锁解锁的开销,同时保证线程安全。

内容的提问来源于stack exchange,提问作者John C.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 11:07:34