在线程执行期间更新变量并杀死线程
Updating variables and killing threads during thread execution
我正在做一个 raspberry pi 项目,并希望在我的应用程序中有一个并行线程,该线程以不同的频率闪烁 LED,以适应应用程序的不同状态。我认为这应该由线程来完成...
基本上我有一个枚举和几个结构来定义状态、GPIO 引脚和需要状态时的适当操作。每个状态都会使 LED 闪烁给定次数,并预定义开启和关闭时间。
然后有一个线程跟踪当前状态的全局变量。
我的问题是,使用“睡眠”执行 GPIO 状态更改和 On/OFF 次的函数有时需要突然停止并启动新状态。
如何解决这个问题?下面是我非常简化的代码版本。
enum class Status { // Possible states of the application
Ok = 0,
ErrorNetwork,
NetworkOk
/* and more status! */
};
struct StatusPin { // Define a pin
std::string name;
const int number; // Pin number
};
// Define a pin and its action
struct StatusAction {
std::string name;
const StatusPin pin;
const unsigned int cycles;
const unsigned int millisOn;
const unsigned int millisOff;
};
// Create one pin
const StatusPin G_StatusLed = {
"Status LED",
2
};
// Map for status states
std::map<const Status, StatusAction> G_StatusMap = {
// One short blink
{Status::Ok,
{"Running", G_StatusLed, 1, 250, 0}},
// Blink 3 times with 500ms delay
{Status::ErrorNetwork,
{ "Network Error", G_StatusLed, 3, 500, 500}},
// Blink 6 times, 1 second high, 500ms low
{Status::NetworkOk,
{ "Network OK", G_StatusLed, 6, 1000, 500}}
/* and more states! */
}
const void performStatusAction(const StatusAction &action) {
for (int i = 0; i < action.cycles; i++) {
pi::digitalWrite(action.pin.number, 1); // Set high
// Sleep for high duration
std::this_thread::sleep_for(
std::chrono::milliseconds(action.millisOn));
pi::digitalWrite(action.pin.number, 0); // Set LOW
// Sleep for low duration
std::this_thread::sleep_for(
std::chrono::milliseconds(action.millisOff));
}
}
//------ GLOBAL VARIABLES ------\
std::mutex mutex;
App::StatusAction* G_CurrentAction = nullptr; // Variable for status action
std::atomic<bool> G_AppRunning {true};
std::atomic<bool> G_StatusWorkerBusy {false};
//------- THREAD FUNCTION -------\
void statusWorker() {
while(G_AppRunning) {
mutex.lock();
G_StatusWorkerBusy = true;
if(G_CurrentAction && !G_CurrentAction->name.empty()) {
performStatusAction(*G_CurrentAction);
// If OneShot, set point to null so that it does not repeat
if(G_CurrentAction->type == App::StatusActionType::OneShot) {
G_CurrentAction = nullptr;
} else {
spdlog::warn("StatusWorker NULLPTR");
}
}
G_StatusWorkerBusy = false;
mutex.unlock();
std::this_thread::sleep_for(1ms);
}
}
int main() {
init(); // Setup GPIO mode, etc...
std::thread threadStatus(statusWorker); // Start status thead
// Update thread variable and set
G_CurrentAction= &G_StatusMap.at(Status::Ok);
bool isAppDone = false;
// Main loop
while(!isAppDone) {
// do something long e.g. Check for network failed
// Set status to network error
G_CurrentAction= &G_StatusMap.at(Status::ErrorNetwork);
// Immidieatly after 1 second network is back, set new status
// Pervious status should stop and new status should be set
// How to cancel loop inside the statusWorker performStatusAction() ?
G_CurrentAction= &G_StatusMap.at(Status::NetworkOk);
/*
Do good stuff here and update status thread if necessary
*/
// At some point exit the loop
isAppDone = true;
}
// Clear up stuff
threadStatus.join();
return 0;
}
如果我没理解错的话,我们希望 LED 状态线程在应用程序的总体状态发生变化时随时注意到,因此线程可以相应地更改其 flashing/occulting 模式。
为此,我们可以使用 std::condition_variable
表示谓词“状态已更改”。然后,在状态线程的循环中,只要我们空闲,我们就会在该条件变量上 wait_for
发出信号(而不是 sleep_for
某个时间)。如果状态发生变化,我们将以新的闪烁模式重新开始。
看起来像这样:
std::condition_variable status_cv;
std::mutex m;
....
// inside status thread
while (G_AppRunning) {
std::unique_lock<std::mutex> locked(m);
// what is app state when we enter this pass?
Status status = G_CurrentStatus;
ActionStatus action = G_StatusMap.at(status);
for (int i = 0; i < action.cycles; i++) {
LED_on();
if (status_cv.wait_for(locked, action.millisOn, [] { return G_CurrentStatus != status; })) {
break; // start anew with a status pattern
}
LED_off();
if (status_cv.wait_for(locked, action.millisOff, [] { return G_CurrentStatus != status; })) {
break; // new state
}
}
}
我正在做一个 raspberry pi 项目,并希望在我的应用程序中有一个并行线程,该线程以不同的频率闪烁 LED,以适应应用程序的不同状态。我认为这应该由线程来完成...
基本上我有一个枚举和几个结构来定义状态、GPIO 引脚和需要状态时的适当操作。每个状态都会使 LED 闪烁给定次数,并预定义开启和关闭时间。
然后有一个线程跟踪当前状态的全局变量。
我的问题是,使用“睡眠”执行 GPIO 状态更改和 On/OFF 次的函数有时需要突然停止并启动新状态。
如何解决这个问题?下面是我非常简化的代码版本。
enum class Status { // Possible states of the application
Ok = 0,
ErrorNetwork,
NetworkOk
/* and more status! */
};
struct StatusPin { // Define a pin
std::string name;
const int number; // Pin number
};
// Define a pin and its action
struct StatusAction {
std::string name;
const StatusPin pin;
const unsigned int cycles;
const unsigned int millisOn;
const unsigned int millisOff;
};
// Create one pin
const StatusPin G_StatusLed = {
"Status LED",
2
};
// Map for status states
std::map<const Status, StatusAction> G_StatusMap = {
// One short blink
{Status::Ok,
{"Running", G_StatusLed, 1, 250, 0}},
// Blink 3 times with 500ms delay
{Status::ErrorNetwork,
{ "Network Error", G_StatusLed, 3, 500, 500}},
// Blink 6 times, 1 second high, 500ms low
{Status::NetworkOk,
{ "Network OK", G_StatusLed, 6, 1000, 500}}
/* and more states! */
}
const void performStatusAction(const StatusAction &action) {
for (int i = 0; i < action.cycles; i++) {
pi::digitalWrite(action.pin.number, 1); // Set high
// Sleep for high duration
std::this_thread::sleep_for(
std::chrono::milliseconds(action.millisOn));
pi::digitalWrite(action.pin.number, 0); // Set LOW
// Sleep for low duration
std::this_thread::sleep_for(
std::chrono::milliseconds(action.millisOff));
}
}
//------ GLOBAL VARIABLES ------\
std::mutex mutex;
App::StatusAction* G_CurrentAction = nullptr; // Variable for status action
std::atomic<bool> G_AppRunning {true};
std::atomic<bool> G_StatusWorkerBusy {false};
//------- THREAD FUNCTION -------\
void statusWorker() {
while(G_AppRunning) {
mutex.lock();
G_StatusWorkerBusy = true;
if(G_CurrentAction && !G_CurrentAction->name.empty()) {
performStatusAction(*G_CurrentAction);
// If OneShot, set point to null so that it does not repeat
if(G_CurrentAction->type == App::StatusActionType::OneShot) {
G_CurrentAction = nullptr;
} else {
spdlog::warn("StatusWorker NULLPTR");
}
}
G_StatusWorkerBusy = false;
mutex.unlock();
std::this_thread::sleep_for(1ms);
}
}
int main() {
init(); // Setup GPIO mode, etc...
std::thread threadStatus(statusWorker); // Start status thead
// Update thread variable and set
G_CurrentAction= &G_StatusMap.at(Status::Ok);
bool isAppDone = false;
// Main loop
while(!isAppDone) {
// do something long e.g. Check for network failed
// Set status to network error
G_CurrentAction= &G_StatusMap.at(Status::ErrorNetwork);
// Immidieatly after 1 second network is back, set new status
// Pervious status should stop and new status should be set
// How to cancel loop inside the statusWorker performStatusAction() ?
G_CurrentAction= &G_StatusMap.at(Status::NetworkOk);
/*
Do good stuff here and update status thread if necessary
*/
// At some point exit the loop
isAppDone = true;
}
// Clear up stuff
threadStatus.join();
return 0;
}
如果我没理解错的话,我们希望 LED 状态线程在应用程序的总体状态发生变化时随时注意到,因此线程可以相应地更改其 flashing/occulting 模式。
为此,我们可以使用 std::condition_variable
表示谓词“状态已更改”。然后,在状态线程的循环中,只要我们空闲,我们就会在该条件变量上 wait_for
发出信号(而不是 sleep_for
某个时间)。如果状态发生变化,我们将以新的闪烁模式重新开始。
看起来像这样:
std::condition_variable status_cv;
std::mutex m;
....
// inside status thread
while (G_AppRunning) {
std::unique_lock<std::mutex> locked(m);
// what is app state when we enter this pass?
Status status = G_CurrentStatus;
ActionStatus action = G_StatusMap.at(status);
for (int i = 0; i < action.cycles; i++) {
LED_on();
if (status_cv.wait_for(locked, action.millisOn, [] { return G_CurrentStatus != status; })) {
break; // start anew with a status pattern
}
LED_off();
if (status_cv.wait_for(locked, action.millisOff, [] { return G_CurrentStatus != status; })) {
break; // new state
}
}
}