๐ป Programming ยท Engineer ยท Intermediate โ Advanced
PROS Tasks & Multitasking
The bridge between inline concurrent actions and real task architecture. Run your intake, sensors, and autonomous simultaneously โ without blocking your main loop.
Before this guide: Read concurrent-actions.html. Tasks are for situations where inline pid_wait_until patterns aren't enough โ typically when a subsystem needs to run continuously and independently of whatever the main code is doing.
A pros::Task is a separate thread of execution. It runs its own function loop in parallel with main. The PROS kernel schedules tasks based on priority and gives each a slice of CPU time every millisecond. You can have multiple tasks running simultaneously โ PROS handles the switching.
// Task function โ must be void and take void*
void intakeTask(void* param) {
while (true) {
if (intakeWanted) {
intake.move_velocity(600);
} else {
intake.brake();
}
pros::delay(10); // ALWAYS delay โ never spin-wait
}
}
// Start the task โ do this once in initialize() or autonomous()
pros::Task myIntake(intakeTask);
Always include pros::delay() inside every task loop. A task without delay will spin at full CPU speed and starve other tasks โ including your drive code.
๐ง Real Example โ Intake + Autonomous Together
// globals.hpp
extern bool intakeOn;
extern bool intakeReverse;
pros::Mutex intakeMtx; // protects shared state
// intake_task.cpp
void intake_task_fn(void*) {
while (true) {
intakeMtx.take(TIMEOUT_MAX);
bool on = intakeOn;
bool rev = intakeReverse;
intakeMtx.give();
if (on) {
intake.move_velocity(rev ? -600 : 600);
} else {
intake.brake();
}
pros::delay(10);
}
}
// autonomous() โ drive and intake run at the same time
void autonomous() {
pros::Task it(intake_task_fn);
// Turn on intake, then immediately start driving
intakeMtx.take(TIMEOUT_MAX);
intakeOn = true;
intakeMtx.give();
chassis.pid_drive_set(24, 110);
chassis.pid_wait(); // intake runs during this wait
intakeMtx.take(TIMEOUT_MAX);
intakeOn = false;
intakeMtx.give();
}
๐ When Do You Need a Mutex?
A mutex (mutual exclusion lock) prevents two tasks from reading and writing a shared variable at the same moment. Without one, you can get data corruption โ your task reads a value halfway through main updating it.
- Use a mutex when two tasks read/write the same variable (e.g.,
intakeOn, targetPos)
- Skip the mutex when only one task writes a variable and others only read (most simple flag variables are fine)
- Never hold a mutex longer than necessary โ take it, read/write, give it immediately
โ The 3 Most Common Task Bugs
โ Task runs once and dies (no while loop)
Symptom: Intake activates briefly at auton start then stops. Fix: Every task function must have while(true) with a pros::delay(10) inside. Without the loop, the function exits and the task ends.
โ Robot becomes unresponsive or crashes
Symptom: Robot freezes mid-auton or driver control stops responding. Fix: Missing pros::delay() in a task loop. Add pros::delay(10); as the last line of every task's while loop, always.
โ Variable changes aren't seen by the task
Symptom: You set intakeOn = true in autonomous but the intake task doesn't respond. Fix: Declare shared variables as volatile or use a mutex. The compiler may optimize away reads of non-volatile variables in tight loops.
๐ Task vs. Inline โ When to Use Each
| Pattern |
Use When |
| pid_wait_until() | You want to trigger something mid-movement (e.g., extend arm at 12 inches into a 24-inch drive) |
| pros::Task | A subsystem needs to run continuously regardless of what else is happening (intake, sensor logging, state machines) |
| pros::delay() in opcontrol | Reading controller inputs in the main opcontrol loop โ 20ms delay gives 50Hz update rate |
PROS Tasks apply concurrent programming — executing multiple code paths simultaneously on a shared-memory system. Concurrency introduces race conditions: when two tasks access the same sensor without synchronization, read/write operations can interleave, producing unpredictable values. The solution is a mutex: only one task can hold the mutex at a time, serializing access to shared resources.
🎤 Interview line: “We use PROS Tasks for sensor monitoring and drive control simultaneously, with mutexes protecting shared sensor variables. When we first implemented concurrent tasks without synchronization, we saw intermittent erratic sensor readings — the classic symptom of a race condition. Adding mutex protection eliminated the issue. We documented this in our programming log because it demonstrates advanced concurrency understanding.”
You add a PROS Task for continuous sensor monitoring. The main autonomous reads erratic sensor values. What is the most likely cause?
⬛ Tasks run too slowly to capture sensor data in real time
⬛ The Task and main thread access the same sensor concurrently without synchronization — a race condition produces inconsistent reads
⬛ PROS Tasks cannot access sensors — only the main function can
📝Notebook entry tip: Build & Program — Orange slide — Write a programming architecture entry when you first implement PROS Tasks: which subsystems run as tasks, why they need concurrent execution, and how you prevent race conditions (mutexes, task priorities). Documenting your concurrency design shows judges university-level CS thinking applied to your robot — an impressive and rarely seen notebook entry.