0 / 6 页已完成

后台线程Background Threads

5.1 为什么需要线程?5.1 Why Do We Need Threads?

到目前为止,我们的代码都是从上往下一行行执行的。但比赛的时候,机器人需要同时做好几件事So far, our code has been running line by line from top to bottom. But during a match, the robot needs to do several things at once:

如果全部写在一个 while(true) 里,代码会又长又乱。更关键的是:如果某个动作需要等待(比如 wait(2, seconds)),其他所有动作都得跟着等。If everything is in one while(true), the code becomes long and messy. More critically: if one action needs to wait (like wait(2, seconds)), every other action has to wait too.

线程 = 多条流水线同时运行Threads = Multiple Assembly Lines Running Simultaneously

你可以把线程想象成多条流水线。主循环是"1 号流水线",你可以开更多的流水线让它们同时干活。每条流水线独立执行自己的代码,互不阻塞。Think of threads as multiple assembly lines. The main loop is "line #1," and you can start more lines that work at the same time. Each line runs its own code independently, without blocking the others.

5.2 创建线程5.2 Creating Threads

先写一个函数,然后把它放到后台去跑:Write a function first, then run it in the background:

C++ (VEXcode)
// 要在后台运行的函数
void liftControl() {
  while (true) {
    // 根据状态控制升降臂
    switch (liftState) {
      case LiftState::DOWN:
        LiftMotor.spinToPosition(0, degrees, false);
        break;
      case LiftState::MIDDLE:
        LiftMotor.spinToPosition(90, degrees, false);
        break;
      case LiftState::UP:
        LiftMotor.spinToPosition(180, degrees, false);
        break;
    }
    wait(20, msec);  // 每 20ms 检查一次
  }
}

int main() {
  // 启动后台线程
  thread liftThread = thread(liftControl);

  // 主循环:处理遥控器
  while (true) {
    // 底盘控制代码...
    // 按钮切换 liftState...
    wait(20, msec);
  }
}
C++ (VS Code)
// 要在后台运行的函数
void liftControl() {
  while (true) {
    // 根据状态控制升降臂
    switch (liftState) {
      case LiftState::DOWN:
        LiftMotor.spinToPosition(0, degrees, false);
        break;
      case LiftState::MIDDLE:
        LiftMotor.spinToPosition(90, degrees, false);
        break;
      case LiftState::UP:
        LiftMotor.spinToPosition(180, degrees, false);
        break;
    }
    wait(20, msec);  // 每 20ms 检查一次
  }
}

int main() {
  // 启动后台线程
  thread liftThread = thread(liftControl);

  // 主循环:处理遥控器
  while (true) {
    // 底盘控制代码...
    // 按钮切换 liftState...
    wait(20, msec);
  }
}
C++ (PROS)
// 要在后台运行的函数
void liftControl() {
  while (true) {
    // 根据状态控制升降臂
    switch (liftState) {
      case LiftState::DOWN:
        LiftMotor.move_absolute(0, 100);
        break;
      case LiftState::MIDDLE:
        LiftMotor.move_absolute(90, 100);
        break;
      case LiftState::UP:
        LiftMotor.move_absolute(180, 100);
        break;
    }
    pros::delay(20);  // 每 20ms 检查一次
  }
}

void opcontrol() {
  // 启动后台线程
  pros::Task liftTask(liftControl);

  // 主循环:处理遥控器
  while (true) {
    // 底盘控制代码...
    // 按钮切换 liftState...
    pros::delay(20);
  }
}

5.3 最简用法5.3 Simplest Usage

后台线程的标准模式就三步:The standard pattern for background threads is three steps:

  1. 写一个函数,里面有 while(true) 循环Write a function with a while(true) loop inside
  2. 循环里做你要做的事Do your work inside the loop
  3. 循环末尾加 wait(20, msec)pros::delay(20)Add wait(20, msec) or pros::delay(20) at the end of the loop
为什么必须加 delay?Why Must We Add a Delay?

如果不加 delay,这个 while(true) 会疯狂运行,占满 CPU,导致其他线程(包括主循环)卡死。20ms 的延迟足够让其他线程有机会运行。Without a delay, the while(true) runs at full speed, maxing out the CPU and freezing other threads (including the main loop). A 20ms delay gives other threads a chance to run.

5.4 线程间通信5.4 Communication Between Threads

主循环和后台线程怎么协作?答案非常简单:共享变量How do the main loop and background threads work together? The answer is simple: shared variables.

C++ (VEXcode)
// 全局变量 — 主循环和后台线程都能访问
LiftState liftState = LiftState::DOWN;

// 后台线程:读 liftState,控制电机
void liftControl() {
  while (true) {
    switch (liftState) { /* ... */ }
    wait(20, msec);
  }
}

// 主循环:改 liftState
while (true) {
  if (Controller1.ButtonR1.PRESSED) {
    liftState = LiftState::UP;  // 改变量,后台线程自动响应
  }
  wait(20, msec);
}
C++ (VS Code)
// 全局变量 — 主循环和后台线程都能访问
LiftState liftState = LiftState::DOWN;

// 后台线程:读 liftState,控制电机
void liftControl() {
  while (true) {
    switch (liftState) { /* ... */ }
    wait(20, msec);
  }
}

// 主循环:改 liftState
while (true) {
  if (currR1 && !lastR1) {
    liftState = LiftState::UP;  // 改变量,后台线程自动响应
  }
  wait(20, msec);
}
C++ (PROS)
// 全局变量 — 主循环和后台线程都能访问
LiftState liftState = LiftState::DOWN;

// 后台线程:读 liftState,控制电机
void liftControl() {
  while (true) {
    switch (liftState) { /* ... */ }
    pros::delay(20);
  }
}

// 主循环:改 liftState
while (true) {
  if (master.get_digital_new_press(DIGITAL_R1)) {
    liftState = LiftState::UP;  // 改变量,后台线程自动响应
  }
  pros::delay(20);
}

主循环改变量,后台线程读变量。简单直接。The main loop changes the variable, the background thread reads it. Simple and straightforward.

5.5 最重要的规则5.5 The Most Important Rule

绝对不要在两个线程里同时控制同一个电机!NEVER Control the Same Motor from Two Threads!

如果主循环说"电机正转",后台线程说"电机反转",电机会疯狂抖动,可能烧坏。If the main loop says "motor forward" and the background thread says "motor reverse," the motor will shake violently and could burn out.

正确做法:一个电机只在一个地方控制。用全局变量(比如状态机的 state)在线程间传递"意图",由负责那个电机的线程来执行具体动作。The right way: each motor is controlled in only one place. Use global variables (like a state machine's state) to pass "intent" between threads, and let the thread responsible for that motor execute the actual commands.

5.6 典型的线程分工5.6 Typical Thread Responsibilities

比赛代码的常见结构Common Structure in Competition Code

底盘由主循环直接控制(因为要实时响应摇杆),机构放后台(因为它们自己跑就行)。The drivetrain is controlled directly by the main loop (needs real-time joystick response), while mechanisms go in the background (they run on their own).

检查点:预测执行顺序Checkpoint: Predict the Execution Order
主循环和后台线程同时在跑。主循环里有 wait(2, seconds)(等 2 秒)。这 2 秒期间,后台线程会怎样?The main loop and background thread are both running. The main loop has wait(2, seconds). During these 2 seconds, what happens to the background thread?