线程上下文切换是什么,和进程上下文切换有什么区别?(新手必看)
线程上下文切换是指在多线程操作系统中,CPU 从一个线程切换到另一个线程执行的过程。
与进程上下文切换相似,线程上下文切换涉及保存当前线程的状态(这里的“状态”指的是线程特有的信息,如线程的程序计数器、寄存器集、栈指针)并恢复另一个线程的状态的操作,以便后者可以继续执行。
线程上下文切换通常比进程上下文切换要轻量,原因在于同一进程内的线程共享相同的进程资源。因此,切换线程不需要切换内存空间和 I/O 环境,这降低了切换的开销。
但是,线程上下文切换仍然需要处理以下内容:
触发线程上下文切换的原因通常有以下几个:
在 Java 中,通过使用多线程并在其中执行一些阻塞操作或使用 sleep() 让线程睡眠,都会触发线程上下文切换。我们来看一个 Java 示例,示例代码如下:
两个线程的逻辑差异将会导致操作系统进行线程上下文切换:
注意,虽然上述示例可能会引发线程上下文切换,但实际发生切换的频率和时机取决于许多因素,包括操作系统的调度策略、系统负载、线程的优先级等。
综上所述,线程共享相同的地址空间和资源,而进程拥有独立的地址空间和资源,因此线程上下文切换是同一进程内不同线程之间的切换,涉及较少的状态变更,成本较低。而进程上下文切换是不同进程之间的切换,涉及全面的地址空间和资源的变更,成本较高。
在设计高性能应用程序时,通常会考虑使用多线程而不是多进程,以减少上下文切换的开销。
与进程上下文切换相似,线程上下文切换涉及保存当前线程的状态(这里的“状态”指的是线程特有的信息,如线程的程序计数器、寄存器集、栈指针)并恢复另一个线程的状态的操作,以便后者可以继续执行。
线程上下文切换通常比进程上下文切换要轻量,原因在于同一进程内的线程共享相同的进程资源。因此,切换线程不需要切换内存空间和 I/O 环境,这降低了切换的开销。
但是,线程上下文切换仍然需要处理以下内容:
- CPU 寄存器集:执行线程中计算的核心部分,包括程序计数器、栈指针、条件码以及通用目的寄存器等;
- 线程栈:每个线程有自己的栈,它用于存储局部变量、函数调用返回地址等。线程切换时,需要更新栈指针以反映新线程的栈状态;
- 线程特定数据(Thread-Specific Data,TSD):线程存储的其运行过程中的特有数据(如错误代码等)。
触发线程上下文切换的原因通常有以下几个:
- 多线程调度:操作系统根据线程的优先级和策略,决定哪个线程应该使用 CPU;
- 阻塞操作:如果线程执行了阻塞操作(如等待 I/O、获取无法立即获得的锁),操作系统会切换到另一个线程,以充分利用 CPU 资源;
- 时间片用尽:线程获得的 CPU 资源是有限的。当时间片用尽,操作系统会进行上下文切换,让另一个线程运行;
- 高优先级线程就绪:当一个高优先级线程从阻塞状态切换为就绪状态时,操作系统可能会打断当前线程,切换到高优先级线程运行。
在 Java 中,通过使用多线程并在其中执行一些阻塞操作或使用 sleep() 让线程睡眠,都会触发线程上下文切换。我们来看一个 Java 示例,示例代码如下:
public class ThreadSwitchExample {
public static void main(String[] args) {
// 创建并启动一个计算线程
Thread computationThread = new Thread(() -> {
long sum = 0;
for(long i = 0; i < 1000000L; i++) {
sum += i;
// 每计算一定次数,线程休眠一小段时间,增加上下文切换的可能性
if(i%1000 == 0) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 重新设置中断状态
System.out.println("Computation thread was interru pted.");
return;
}
}
}
System.out.println("Computation finished: " + sum);
});
// 创建并启动一个等待用户输入的线程
Thread inputThread = new Thread(() -> {
try {
System.out.println("Waiting for user input:");
int read = System.in.read(); // 线程执行阻塞操作,等待用户输入
System.out.println("You entered: " + (char) read);
} catch (IOException e) {
System.out.println("An error occurred while reading input.");
}
});
// 启动线程
computationThread.start();
inputThread.start();
// 等待两个线程完成
try {
computationThread.join();
inputThread.join();
} catch (InterruptedException e) {
System.out.println("Main thread was interrupted.");
}
System.out.println("Main thread finished.");
}
}
在上述代码中,main() 方法主线程启动了两个线程:
- 一个是 computationThread 线程(用于进行计算);
- 另一个是 inputThread 线程(用于等待用户输入)。
两个线程的逻辑差异将会导致操作系统进行线程上下文切换:
- computationThread 在计算过程中会间歇性休眠,休眠是通过 sleep(1) 方法实现的。这种休眠会使得操作系统有机会将 CPU 资源分配给其他的线程,从而引发线程上下文切换;
- inputThread 使用 System.in.read() 方法执行阻塞操作直到接收到输入。在该线程等待用户输入期间,操作系统可能会将 CPU 资源分配给其他线程。
注意,虽然上述示例可能会引发线程上下文切换,但实际发生切换的频率和时机取决于许多因素,包括操作系统的调度策略、系统负载、线程的优先级等。
综上所述,线程共享相同的地址空间和资源,而进程拥有独立的地址空间和资源,因此线程上下文切换是同一进程内不同线程之间的切换,涉及较少的状态变更,成本较低。而进程上下文切换是不同进程之间的切换,涉及全面的地址空间和资源的变更,成本较高。
在设计高性能应用程序时,通常会考虑使用多线程而不是多进程,以减少上下文切换的开销。
ICP备案:
公安联网备案: