异步管理

异步指的是当前的请求不能立刻得到满足,需要等待一个事件,当事件发生时,才能继续往下执行。这里我们以一个异步系统调用为例进行说明:

#[no_mangle]
pub fn main() -> i32 {
    let v = vec![
        thread_create(thread_a as usize, 0),
        thread_create(thread_b as usize, 0),
        thread_create(thread_c as usize, 0),
    ];
    for tid in v.iter() {
        let exit_code = thread_join(*tid as usize);
        println!("thread#{} exited with code {}", tid, exit_code);
    }
    println!("main thread exited");
    0
}

这是一个用户态使用多线程的例子,可见主线程创建了三个线程,并在程序退出前等待所有子线程退出。

thread_join最终在内核的实现如下:

/// 主线程等待tid线程
///
/// 若不是主线程调用,就报错并返回usize::MAX
pub fn sys_thread_join(tid: usize) -> (usize, usize) {
    // 只允许主线程调用
    ....
    if cur_tid != 0 {
        println!(
            "[Kernel] Thread join failed, can only be called by root thread, current tid: {}",
            cur_tid
        );
        return (usize::MAX, 0);
    }
    // 获取tid对应的线程
    ....

    // 创建等待协程
    current_thread.set_state(ThreadState::Waiting);
    executor::spawn(WaitForThread::new(current_thread, waited_thread));
    return (0, 0);
}

我们知道,一个进程中可能有多个正在运行的线程,一般来说,主线程负责回收所有的线程,当所有线程都退出后,主线程才能退出。

这里主线程等待其他线程退出,但是当主线程调用这个函数时,很可能发生子线程还在运行的情况,这就是一个典型的异步请求。

如何来实现一个异步的请求,这就是本章探讨的问题,我们将在后面几节中逐步介绍,这里首先可以看到我们将主线程设置为了Waiting异步等待状态,并生成了一个WaitForThread实例并加入到协程执行器(执行器)中,实际上这是一个协程,在下一节异步协程中就会详细说明。

完成协程创建后,这个函数就直接返回了,在调度一节我们知道,调度器不会调度Waiting状态的线程,所以主线程暂时挂起了,不会被执行,但问题是什么时候它才会被唤醒呢?在后面几节我们就能找到答案。