最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

multithreading - How to run task in tokio multiple thread with a specific running tasks number only - Stack Overflow

programmeradmin2浏览0评论

I am working on a project that needs to run some tasks in parallel with a specific maximum running tasks only (for example: 10 tasks). I tried some methods, but the problem was that the tasks started immediately after creation. For example:

#[tokio::main(flavor = "multi_thread", worker_threads = 10)]
async fn main() {
    let mut tasks = vec![];
    for i in 0..100 {
        let task = tokio::spawn(async move {
            println!("This proceess: {}", i);
            i
        });
        tasks.push(task);
    }

    let mut results = vec![];
    for task in tasks {
        let result = task.await.unwrap();
        results.push(result);
    }
    dbg!(&results);
}

Do we have any way to spawn the task without running it immediately? I want to achieve something like: running 100 tasks in 10 different threads. The tasks are only started if the other tasks in 10 threads are completed.

I am working on a project that needs to run some tasks in parallel with a specific maximum running tasks only (for example: 10 tasks). I tried some methods, but the problem was that the tasks started immediately after creation. For example:

#[tokio::main(flavor = "multi_thread", worker_threads = 10)]
async fn main() {
    let mut tasks = vec![];
    for i in 0..100 {
        let task = tokio::spawn(async move {
            println!("This proceess: {}", i);
            i
        });
        tasks.push(task);
    }

    let mut results = vec![];
    for task in tasks {
        let result = task.await.unwrap();
        results.push(result);
    }
    dbg!(&results);
}

Do we have any way to spawn the task without running it immediately? I want to achieve something like: running 100 tasks in 10 different threads. The tasks are only started if the other tasks in 10 threads are completed.

Share Improve this question asked Mar 12 at 15:46 Kingfisher PhuocKingfisher Phuoc 8,22010 gold badges52 silver badges95 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 3

Tokio, like most async runtimes, doesn't provide a 1:1 tasks to threads guarantee - in fact, one of the major motivations for such runtimes is to do hundreds or thousands of async tasks at the same time using just a handful of async threads. The choice of 10 threads isn't a limit on how many concurrent async tasks can execute - it's only a limit on the underlying resources (OS threads) that tokio's scheduler is permitted to use in order to run its assigned tasks.

In fact, tokio has a single-threaded runtime mode, where a single runtime thread handles a large number of concurrent async tasks, scheduling them on that one thread and switching from one to the other at await points (making them appear to run concurrently).^

If you need to prevent some async action from happening more than 10x concurrency for application reasons (or because you need to limit the concurrency on some external resource), you could use a semaphore:

extern crate tokio;

#[tokio::main(flavor = "multi_thread", worker_threads = 10)]
async fn main() {
    let sem = std::sync::Arc::new(tokio::sync::Semaphore::new(10));
    let mut tasks = vec![];
    for i in 0..20 {
        let sem_clone = sem.clone();
        let task = tokio::spawn(async move {
            let permit = sem_clone.acquire().await.unwrap();
            println!("This proceess: {}, now: {:?}", i, std::time::Instant::now());
            tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
            
            // Permit is dropped here
            i
        });
        tasks.push(task);
    }

    let mut results = vec![];
    for task in tasks {
        let result = task.await.unwrap();
        results.push(result);
    }
    dbg!(&results);
}

With that said, it should be perfectly fine to have more than 10 tasks on the runtime (in the sense that they were spawned) as long as they are indeed io/await-bound; chances are that if you're worried about limiting tasks for the sake of preserving worker threads, you may want to use a different spawning strategy altogether - in that case this answer gives some alternate suggestions:

  • rayon if they're CPU-heavy tasks
  • spawn_blocking if they're likely to block in non-async IO/non-async mutex
  • separate threads for long-lived tasks that do blocking IO work

^ As a case study, I have a game that runs hundreds of async tasks on anywhere from 1 to 20 cores and OS threads; many of those tasks are blocked on either player events, timers, etc and so they don't consume one of the few OS threads most of the time.

发布评论

评论列表(0)

  1. 暂无评论