深入解析线程上下文切换:掌握线程上下文切换的核心原理

1. 进程与线程的基本概念

1.1 进程与线程的区别

在操作系统中,进程和线程是两个基本的概念,它们共同构成了程序的执行环境。了解它们的区别是理解线程上下文切换的基础。

  • 进程:进程是程序的一次执行实例。它是操作系统资源分配的基本单位。每个进程都有自己的内存空间、文件描述符、全局变量等系统资源。
  • 线程:线程是进程中的一个执行单元。一个进程可以包含多个线程,它们共享进程的资源(如内存、文件描述符等),但每个线程有自己的寄存器、堆栈和程序计数器。

进程和线程的主要区别在于:

  • 进程是独立的执行单元,而线程是进程中的一个执行单元。
  • 进程间的切换开销较大,因为需要切换独立的内存空间,而线程间的切换开销较小,因为它们共享内存空间。

以下是一个简单的Java代码示例,演示如何创建进程和线程:

public class ProcessThreadDemo {public static void main(String[] args) {// 创建并启动线程Thread thread = new Thread(() -> {System.out.println("This is a thread running.");});thread.start();// 创建并启动进程ProcessBuilder processBuilder = new ProcessBuilder("notepad.exe");try {Process process = processBuilder.start();System.out.println("This is a process running.");} catch (IOException e) {e.printStackTrace();}}
}

1.2 线程的生命周期

线程的生命周期包括以下几个状态:

  • 新建(New):线程被创建,但尚未启动。
  • 就绪(Runnable):线程已经启动,等待CPU的调度。
  • 运行(Running):线程获得CPU时间片,正在执行。
  • 阻塞(Blocked):线程正在等待某种条件(如I/O操作)完成。
  • 终止(Terminated):线程执行结束。

在Java中,可以使用Thread类的状态枚举来查看线程的当前状态:

public class ThreadLifecycleDemo {public static void main(String[] args) {Thread thread = new Thread(() -> {System.out.println("Thread is running.");});System.out.println("Thread state: " + thread.getState()); // NEWthread.start();System.out.println("Thread state: " + thread.getState()); // RUNNABLE}
}

2. 线程上下文的定义

2.1 上下文包含的内容

线程上下文是指线程在执行过程中需要保存和恢复的一组信息,这些信息使得线程可以在被中断后恢复执行。具体包括以下内容:

  • 程序计数器(Program Counter, PC):保存线程当前执行到的指令位置。
  • 寄存器(Registers):保存线程执行过程中使用的所有寄存器的值。
  • 堆栈(Stack):保存线程的调用栈,包括局部变量和方法调用信息。
  • 线程控制块(Thread Control Block, TCB):保存线程的各种状态信息,如线程ID、优先级、调度信息等。

这些信息构成了线程的“上下文”,当线程切换时,操作系统需要保存当前线程的上下文,并恢复即将执行线程的上下文。

2.2 上下文的重要性

上下文对于线程切换至关重要,因为它保证了线程的执行连续性和正确性。当一个线程被中断时,它的上下文被保存,以便在它再次运行时可以从中断点继续执行,而不会丢失任何重要的信息。上下文切换的正确性和效率直接影响系统的性能和稳定性。
例如,在多线程环境下,如果没有正确保存和恢复上下文,线程间的计算结果可能会出现混乱,导致程序运行结果不正确。这种情况在并发编程中尤为关键。
下面是一个Java代码示例,演示如何在线程间共享数据,并显示上下文的重要性:

public class ThreadContextDemo {private static int sharedCounter = 0;public static void main(String[] args) {Runnable task = () -> {for (int i = 0; i < 1000; i++) {incrementCounter();}};Thread thread1 = new Thread(task);Thread thread2 = new Thread(task);thread1.start();thread2.start();try {thread1.join();thread2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Final counter value: " + sharedCounter); // 结果可能不是2000,取决于上下文切换}private synchronized static void incrementCounter() {sharedCounter++;}
}

在上述代码中,sharedCounter是两个线程共享的数据。如果没有正确的上下文切换和同步机制,最终的计数结果可能不正确。因此,理解和管理上下文在并发编程中非常重要。

3. 寄存器与程序计数器

3.1 寄存器的作用

寄存器是处理器内部的高速存储单元,用于暂存处理器在执行指令时需要快速访问的数据。寄存器的种类和数量因处理器架构不同而有所差异,但通常包括以下几类:

  • 通用寄存器(General-purpose registers):用于存放整数运算的操作数和结果。
  • 浮点寄存器(Floating-point registers):用于存放浮点运算的操作数和结果。
  • 指针寄存器(Pointer registers):用于存放内存地址。
  • 状态寄存器(Status registers):用于存放处理器当前的状态信息。

在上下文切换时,所有这些寄存器的内容都需要保存和恢复,以确保线程在切换后能继续正确执行。以下是一个简单的示例,展示了Java程序如何利用寄存器进行运算。虽然Java代码本身不能直接操作寄存器,但它反映了寄存器的工作原理:

public class RegisterDemo {public static void main(String[] args) {int a = 10;int b = 20;int c = a + b; // 在底层,a 和 b 的值会被加载到寄存器中进行加法运算System.out.println("Result: " + c);}
}

3.2 程序计数器的作用

程序计数器(Program Counter, PC)是一个寄存器,用于存放当前正在执行的指令地址。在每次指令执行后,程序计数器的值会自动更新,以指向下一条指令。程序计数器在上下文切换中的作用至关重要,因为它记录了线程的执行位置,使得线程在被切换后可以从正确的位置继续执行。以下是一个Java示例,展示了程序计数器的工作方式:

public class ProgramCounterDemo {public static void main(String[] args) {int x = 5;int y = 10;int result = add(x, y);System.out.println("Result: " + result);}public static int add(int a, int b) {return a + b; // 程序计数器指向这里,然后返回到 main 方法继续执行}
}

在这个示例中,当调用add方法时,程序计数器会指向add方法的开始位置,并在执行完add方法后返回main方法继续执行。程序计数器确保了程序的执行顺序和流程的正确性。

4. 线程控制块(TCB)

4.1 TCB的结构

线程控制块(Thread Control Block, TCB)是操作系统用来管理线程的一个数据结构。TCB包含了与线程相关的所有信息,使操作系统能够在上下文切换时正确地保存和恢复线程的状态。TCB的主要组成部分包括:

  • 线程ID:唯一标识线程的ID。
  • 线程状态:线程的当前状态(如新建、就绪、运行、阻塞、终止)。
  • 寄存器上下文:线程所有寄存器的值,包括程序计数器。
  • 堆栈指针:指向线程堆栈的指针,保存线程的调用栈信息。
  • 优先级:线程的优先级,用于调度算法。
  • 调度信息:线程被调度器使用的各种信息,如时间片、调度队列位置等。
  • 资源使用信息:线程使用的资源,如打开的文件描述符、内存占用等。

TCB确保了线程的执行环境在上下文切换中能够被完整地保存和恢复,从而保证线程能够正确地继续执行。

4.2 TCB在上下文切换中的作用

在上下文切换过程中,操作系统需要完成以下步骤来保存和恢复线程的状态:

1.保存当前线程的上下文:

  • 保存当前线程所有寄存器的值到其TCB中。
  • 保存当前线程的程序计数器到其TCB中。
  • 更新当前线程的状态到TCB中(如从运行变为就绪)。
  1. 选择下一个线程:
  • 从就绪队列中选择一个新的线程进行执行。选择策略取决于调度算法。

3.恢复新线程的上下文:

  • 从新线程的TCB中恢复寄存器的值。
  • 从新线程的TCB中恢复程序计数器的值。
  • 更新新线程的状态到TCB中(如从就绪变为运行)。

这些步骤确保了线程切换后能够从中断点继续执行,而不会丢失任何重要的信息。以下是一个简化的Java代码示例,展示了线程切换的过程(实际的上下文切换由操作系统管理,这里只是一个模拟):

public class ContextSwitchDemo {static class ThreadControlBlock {int threadId;String state;int programCounter;int[] registers;ThreadControlBlock(int id) {threadId = id;state = "NEW";programCounter = 0;registers = new int[10];}void saveContext(int pc, int[] regs) {programCounter = pc;System.arraycopy(regs, 0, registers, 0, regs.length);state = "SAVED";}void restoreContext() {// 模拟恢复上下文state = "RUNNING";// 恢复程序计数器和寄存器}}public static void main(String[] args) {ThreadControlBlock tcb1 = new ThreadControlBlock(1);ThreadControlBlock tcb2 = new ThreadControlBlock(2);// 模拟线程1执行并切换到线程2tcb1.saveContext(100, new int[]{1, 2, 3});tcb2.restoreContext();System.out.println("Thread 1 state: " + tcb1.state); // 输出: SAVEDSystem.out.println("Thread 2 state: " + tcb2.state); // 输出: RUNNING}
}

在这个示例中,我们创建了两个TCB,并模拟了线程1保存上下文和线程2恢复上下文的过程。这展示了TCB在上下文切换中的作用。

5. 线程上下文切换的过程

5.1 上下文切换的步骤

线程上下文切换是指在多线程操作系统中,CPU从一个线程的上下文切换到另一个线程的上下文的过程。上下文切换需要保存当前线程的状态,并恢复下一个线程的状态,以确保各个线程能够独立且正确地执行。以下是上下文切换的主要步骤:

1.保存当前线程的状态:

  • 保存当前线程的程序计数器,确保线程可以从被中断的位置继续执行。
  • 保存所有的CPU寄存器的值,包括通用寄存器、浮点寄存器等。
  • 更新当前线程的状态(如从运行变为就绪)。
  • 保存线程的堆栈指针,确保调用栈信息不丢失。

2.选择下一个线程:

  • 操作系统的调度器根据调度策略选择下一个线程。
  • 更新新线程的状态(如从就绪变为运行)。
  • 恢复新线程的状态:

3.恢复新线程的程序计数器。

  • 恢复新线程的所有寄存器的值。
  • 恢复新线程的堆栈指针。
  • 整个过程在极短时间内完成,使得用户感觉多个线程是同时执行的。这种并发执行是多线程程序的核心。

5.2 上下文切换的代价

上下文切换虽然使得多线程并发执行成为可能,但也带来了一定的开销。主要包括:

  • 时间开销:保存和恢复寄存器、程序计数器、堆栈指针等需要时间。频繁的上下文切换会导致CPU时间花费在保存和恢复上下文上,而不是实际的线程执行上。
  • 缓存失效:上下文切换可能导致CPU缓存中的数据失效,需要重新加载数据,这会增加内存访问的时间。
  • 调度器开销:操作系统的调度器需要选择下一个要运行的线程,这也需要一定的计算时间。

因此,在设计多线程程序时,尽量减少不必要的上下文切换是提升性能的关键。以下是一个Java代码示例,演示了线程的上下文切换过程:

public class ContextSwitchExample {public static void main(String[] args) {Runnable task1 = () -> {for (int i = 0; i < 5; i++) {System.out.println("Task 1 - Step " + i);try {Thread.sleep(100); // 模拟执行过程中被切换} catch (InterruptedException e) {e.printStackTrace();}}};Runnable task2 = () -> {for (int i = 0; i < 5; i++) {System.out.println("Task 2 - Step " + i);try {Thread.sleep(100); // 模拟执行过程中被切换} catch (InterruptedException e) {e.printStackTrace();}}};Thread thread1 = new Thread(task1);Thread thread2 = new Thread(task2);thread1.start();thread2.start();try {thread1.join();thread2.join();} catch (InterruptedException e) {e.printStackTrace();}}
}

在这个示例中,两个线程交替执行,模拟了上下文切换的过程。虽然上下文切换由操作系统处理,但从输出可以看到,线程间的切换使得它们可以并发执行任务。

6. 引起线程上下文切换的原因

6.1 时间片用完

操作系统采用时间片轮转调度算法时,每个线程都会分配一个固定长度的时间片(Time Slice)。当一个线程的时间片用完后,操作系统会暂停该线程的执行,并将其上下文保存到TCB中,然后调度其他就绪线程执行。这种机制确保了所有线程都有机会获得CPU时间,避免某个线程长期占用CPU资源。以下是一个Java代码示例,演示了使用时间片轮转调度算法的上下文切换:

public class TimeSliceDemo {public static void main(String[] args) {Runnable task = () -> {for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName() + " - Step " + i);try {Thread.sleep(50); // 模拟时间片的结束} catch (InterruptedException e) {e.printStackTrace();}}};Thread thread1 = new Thread(task, "Thread 1");Thread thread2 = new Thread(task, "Thread 2");thread1.start();thread2.start();try {thread1.join();thread2.join();} catch (InterruptedException e) {e.printStackTrace();}}
}

在这个示例中,两个线程交替执行,每个线程在执行一定时间后会让出CPU,模拟了时间片轮转调度。

6.2 I/O操作

当一个线程执行I/O操作(如读写文件、网络通信)时,由于I/O操作速度远慢于CPU速度,线程会进入阻塞状态以等待I/O操作完成。这时,操作系统会进行上下文切换,将CPU分配给其他就绪线程。等I/O操作完成后,被阻塞的线程会进入就绪状态,等待被调度执行。以下是一个Java代码示例,演示了线程在执行I/O操作时的上下文切换:

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;public class IODemo {public static void main(String[] args) {Runnable ioTask = () -> {try (BufferedReader reader = new BufferedReader(new FileReader("example.txt"))) {String line;while ((line = reader.readLine()) != null) {System.out.println(Thread.currentThread().getName() + " - Read line: " + line);}} catch (IOException e) {e.printStackTrace();}};Thread thread1 = new Thread(ioTask, "IO Thread 1");Thread thread2 = new Thread(ioTask, "IO Thread 2");thread1.start();thread2.start();try {thread1.join();thread2.join();} catch (InterruptedException e) {e.printStackTrace();}}
}

在这个示例中,两个线程执行文件读取操作,当一个线程被阻塞等待I/O操作时,另一个线程可以获得CPU执行。

6.3 高优先级线程到来

操作系统通常采用优先级调度算法。当一个高优先级线程就绪时,操作系统会暂停当前正在运行的低优先级线程,进行上下文切换以执行高优先级线程。优先级调度算法保证了高优先级线程能够快速获得CPU资源。以下是一个Java代码示例,演示了高优先级线程引起的上下文切换:

public class PriorityDemo {public static void main(String[] args) {Runnable lowPriorityTask = () -> {for (int i = 0; i < 5; i++) {System.out.println(Thread.currentThread().getName() + " - Low priority step " + i);try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}};Runnable highPriorityTask = () -> {for (int i = 0; i < 5; i++) {System.out.println(Thread.currentThread().getName() + " - High priority step " + i);try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}}};Thread lowPriorityThread = new Thread(lowPriorityTask, "Low Priority Thread");Thread highPriorityThread = new Thread(highPriorityTask, "High Priority Thread");lowPriorityThread.setPriority(Thread.MIN_PRIORITY);highPriorityThread.setPriority(Thread.MAX_PRIORITY);lowPriorityThread.start();highPriorityThread.start();try {lowPriorityThread.join();highPriorityThread.join();} catch (InterruptedException e) {e.printStackTrace();}}
}

在这个示例中,高优先级线程会比低优先级线程更频繁地获得CPU执行时间,模拟了优先级调度引起的上下文切换。

6.4 其他常见原因

除了上述原因,以下情况也会引起线程的上下文切换:

  • 同步锁竞争:多个线程竞争同一个同步锁时,未获得锁的线程会被阻塞,进行上下文切换。
  • 系统调用:线程执行系统调用(如内存分配、进程通信)时,可能会被阻塞,导致上下文切换。
  • 异常处理:线程在执行过程中遇到异常,需要切换到异常处理程序。

理解这些原因有助于优化线程的调度和性能,减少不必要的上下文切换,提高程序的运行效率。

7. 优化线程上下文切换

7.1 减少不必要的上下文切换

上下文切换的代价较高,因此减少不必要的上下文切换是提升系统性能的关键。以下是一些减少上下文切换的方法:

  • 减少线程数量:避免创建过多线程,特别是在CPU核数不多的情况下。线程数量超过CPU核数会导致频繁的上下文切换。
  • 批量处理任务:将多个小任务合并为一个大任务,减少线程切换的次数。例如,在处理网络请求时,可以使用批量处理方式减少线程切换。
  • 使用无锁数据结构:减少锁竞争,使用无锁数据结构(如CAS算法)可以减少线程阻塞,从而减少上下文切换。
  • 适当调整线程优先级:根据任务的重要性设置适当的线程优先级,避免过多的高优先级线程抢占CPU时间。

以下是一个Java代码示例,展示了通过减少线程数量优化上下文切换:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ReduceThreadExample {public static void main(String[] args) {int taskCount = 10;ExecutorService executor = Executors.newFixedThreadPool(4); // 使用固定线程池,减少线程数量for (int i = 0; i < taskCount; i++) {int taskId = i;executor.submit(() -> {System.out.println("Task " + taskId + " is running on " + Thread.currentThread().getName());try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}});}executor.shutdown();}
}

在这个示例中,使用固定线程池控制线程数量,避免创建过多线程,从而减少上下文切换。

7.2 使用合适的锁机制

锁机制在多线程编程中非常重要,但不当使用会导致频繁的上下文切换。以下是一些优化锁机制的方法:

  • 减少锁的粒度:锁的粒度越大,锁的竞争越激烈,导致更多的上下文切换。通过减少锁的粒度,可以降低锁竞争。
  • 使用读写锁:读写锁允许多个读线程并发执行,但在写线程执行时会独占锁。适用于读多写少的场景。
  • 避免嵌套锁:嵌套锁容易导致死锁和频繁的上下文切换,尽量避免使用。

以下是一个Java代码示例,展示了使用读写锁优化上下文切换:

import java.util.concurrent.locks.ReentrantReadWriteLock;public class ReadWriteLockExample {private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();private static int sharedResource = 0;public static void main(String[] args) {Runnable readTask = () -> {lock.readLock().lock();try {System.out.println(Thread.currentThread().getName() + " read: " + sharedResource);} finally {lock.readLock().unlock();}};Runnable writeTask = () -> {lock.writeLock().lock();try {sharedResource++;System.out.println(Thread.currentThread().getName() + " wrote: " + sharedResource);} finally {lock.writeLock().unlock();}};Thread reader1 = new Thread(readTask, "Reader 1");Thread reader2 = new Thread(readTask, "Reader 2");Thread writer = new Thread(writeTask, "Writer");reader1.start();reader2.start();writer.start();}
}

在这个示例中,使用读写锁允许多个读线程并发执行,减少了写线程独占锁导致的上下文切换。

7.3 使用轻量级线程框架

轻量级线程框架(如协程、纤程)能够更高效地管理并执行大量并发任务。相对于传统线程,轻量级线程的上下文切换代价更低,更适合高并发场景。以下是一个Java代码示例,展示了使用轻量级线程框架(如Quasar)的协程:

import co.paralleluniverse.fibers.Fiber;
import co.paralleluniverse.fibers.SuspendExecution;
import co.paralleluniverse.fibers.Suspendable;public class FiberExample {public static void main(String[] args) {for (int i = 0; i < 10; i++) {new Fiber<Void>(() -> {System.out.println("Fiber " + Fiber.currentFiber().getName() + " is running.");return null;}).start();}}
}

在这个示例中,使用Quasar库的Fiber实现轻量级线程,减少了上下文切换的开销。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/18474.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

pytest的断言与Selenium 模拟操作的一个例子

在Python中&#xff0c;pytest是一个流行的单元测试框架&#xff0c;而Selenium是一个用于自动化浏览器操作的工具。结合这两者&#xff0c;我们可以编写自动化测试脚本来验证网页的行为是否符合预期。下面是一个简单的例子&#xff0c;展示了如何使用pytest的断言功能以及Sele…

解决在Mac下使用npm报错:Error: EACCES: permission denied

原因说明&#xff1a;没有足够的权限在 /usr/local/lib/node_modules 目录下创建文件夹 这个错误表明你在安装或更新 Vue.js&#xff08;vue&#xff09;包时&#xff0c;没有足够的权限在 /usr/local/lib/node_modules 目录下创建文件夹。这通常是因为默认情况下&#xff0c;普…

【头歌-Python】文件自学引导

禁止转载&#xff0c;原文&#xff1a;https://blog.csdn.net/qq_45801887/article/details/139258793 参考教程&#xff1a;B站视频讲解——https://space.bilibili.com/3546616042621301 如果代码存在问题&#xff0c;麻烦大家指正 ~ ~有帮助麻烦点个赞 ~ ~ 文件自学引导 第…

算数运算符

算术运算符是用于数值类型变量计算的运算符。 它的返回结果是数值。 赋值符号 关键知识点&#xff1a;先看右侧&#xff0c;再看左侧&#xff0c;把右侧的值赋值给左侧的变量。 附上代码&#xff1a; string myName "唐唐"; int myAge 18; float myHeight 177.5…

202312青少年软件编程(Python)等级考试试卷(四级)

第 1 题 【单选题】 下列有关分治算法思想的描述不正确的是?( ) A :将问题分解成的子问题具有相同的模式 B :将问题分解出的各个子问题相互之间有公共子问题 C :当问题足够小时,可以直接求解 D :可以将子问题的求解结果合并成原问题的解 正确答案:B 试题解析: 第 2…

ADIL简单测试实例

参考资料&#xff1a;https://blog.csdn.net/geyichongchujianghu/article/details/130045373这个连接是Java的代码&#xff0c;我根据它的链接写了一个kotlin版本的。 AIDL&#xff08;Android Interface Definition Language&#xff09;是Android平台上用于进程间通信&…

AI办公自动化:kimi批量新建文件夹

工作任务&#xff1a;批量新建多个文件夹&#xff0c;每个文件夹中的年份不一样 在kimi中输入提示词&#xff1a; 你是一个Python编程专家&#xff0c;要完成一个编写关于录制电脑上的键盘和鼠标操作的Python脚本的任务&#xff0c;具体步骤如下&#xff1a; 打开文件夹&…

FFmpeg编解码的那些事(1)

看了网上很多ffmpeg的编解码的文章和代码&#xff0c;发现有很多文章和代码都过时了&#xff0c;主要还是ffmpeg有很多接口都已经发生变化了。 这里简单说一下&#xff0c;什么是编码和解码。 1.视频编码 对于视频来说&#xff0c;可以理解为多张&#xff08;rgb或者yuv&…

Python散点图矩阵代码模版

本文分享Python seaborn实现散点图矩阵代码模版&#xff0c;节选自&#x1f449;嫌Matplotlib繁琐&#xff1f;试试Seaborn&#xff01; 散点图矩阵&#xff08;scatterplot matrix&#xff09;展示原始数据中所有变量两两之间关系&#xff0c;可以规避单一统计指标的偏差&…

二分查找算法详讲(三种版本写法)原创

介绍: 二分查找算法&#xff08;Binary Search&#xff09;是一种在有序数组中查找目标元素的算法。 它的基本思想是通过将目标元素与数组的中间元素进行比较&#xff0c;从而将搜索范围缩小一半。 如果目标元素等于中间元素&#xff0c;则搜索结束&#xff1b;如果目标元素小…

Neural Filters:照片恢复

Ps菜单&#xff1a;滤镜/Neural Filters/恢复/照片恢复 Neural Filters/RESTORATION/Photo Restoration 照片恢复 Photo Restoration借助 AI 强大功能快速恢复旧照片&#xff0c;提高对比度、增强细节、消除划痕。将此滤镜与着色相结合以进一步增强效果。 “照片恢复”滤镜利用…

Scikit-Learn随机森林

Scikit-Learn随机森林 1、随机森林1.1、集成学习1.2、Bagging方法1.3、随机森林算法1.4、随机森林的优缺点2、Scikit-Learn随机森林回归2.1、Scikit-Learn随机森林回归API2.2、随机森林回归实践(加州房价预测)1、随机森林 随机森林是一种由决策树构成的集成算法,它在大多情况…

mac安装的VMware虚拟机进行桥接模式配置

1、先进行网络适配器选择&#xff0c;选择桥接模式 2、点击网络适配器 设置... 3、选择WiFi&#xff08;我使用的是WiFi&#xff0c;所以选择这个&#xff09;&#xff0c;注意看右边的信息&#xff1a;IP和子网掩码&#xff0c;后续配置虚拟机的ifcfg-ens文件会用到 4、编辑if…

【论文阅读笔记】The Google File System

1 简介 Google File System (GFS) 是一个可扩展的分布式文件系统&#xff0c;专为快速增长的Google数据处理需求而设计。这篇论文发表于2003年&#xff0c;此前已在Google内部大规模应用。 GFS不仅追求性能、可伸缩性、可靠性和可用性等传统分布式文件系统的设计目标&#xf…

benchmark::State benchmark 原理

benchmark::State benchmark::State是Google Benchmark库中的一个核心类&#xff0c;用于管理单个基准测试的状态信息和控制基准测试的执行流程。在编写基准测试时&#xff0c;这个类提供了一套丰富的接口&#xff0c;允许用户获取测试循环的次数、调整测试参数、测量时间等&a…

P9 【力扣+知识点】【算法】【二分查找】C++版

【704】二分查找&#xff08;模板题&#xff09;看到复杂度logN&#xff0c;得想到二分 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜索 nums 中的 target&#xff0c;如果目标值存在返回下标&#xff0…

企业微信hook接口协议,ipad协议http,语音转文字

语音转文字 参数名必选类型说明uuid是String每个实例的唯一标识&#xff0c;根据uuid操作具体企业微信msgid是int要转文字的语音消息id 请求示例 {"uuid":"a4ea6a39-4b3a-4098-a250-2a07bef57355","msgid":1063645 } 返回示例 {"data&…

电源模块测试系统怎么测试输入电压范围?

在现代电子设备中&#xff0c;电源模块的性能直接影响着整个系统的稳定性和效率。其中&#xff0c;电源输入电压范围是指电源能够接受的输入电压的最小值和最大值&#xff0c;它是确保电源正常工作的重要参数。为了提高测试效率和精度&#xff0c;自动化的测试方法逐渐取代了传…

【Game】Rumble Heroes

文章目录 1 英雄2 守护兽3 符文4 祝福5 阵容推荐6 Boss7 兑换码 1 英雄 &#xff08;1&#xff09;力量 神话英雄 圣骑士-乌瑟尔 传说英雄 双刀-宫本武藏死亡骑士-阿萨斯冰霜骑士-亚瑟疾风焰刃-缘壹熊猫武僧-阿宝 史诗英雄 大剑-克劳德狂战士-奎托斯魔山-克里刚猎人-奈辛瓦里 稀…

宝塔部署Java+Vue前后端分离项目

1. 服务器 服务器选择Linux的CentOS7的版本 2. 宝塔Linux面板 2.1 百度搜索宝塔 2.2 进去之后点击立即免费安装 2.3 选择Linux在线安装&#xff0c;输入服务器信息进行安装(也可以选择其他方式) 安装完成之后会弹一个宝塔的应用面板&#xff0c;并附带有登录名称和密码&…