這個問題是從 Code Review Stack Exchange遷移而來的,因為它可以在 Stack Overflow 上得到回答。 8 天前遷移 。
我正在嘗試創建一個基類來管理基于切片的作業負載。
我的方法是創建一個基本抽象類來處理作業的初始化/終止,并在特定類中從該類繼承,這些類只指定實際作業和時間。
如果發生一定數量的錯誤,我還在基類中添加了重新初始化作業負載的功能。
這在一個簡單的示例(如下所示)和我擁有的大多數作業負載中都可以正常作業,但是當我嘗試將其用于特定作業負載(讀取由 arduino 寫入的串行埠)時,它完全弄亂了從阿杜諾。
我懷疑我的方法有問題,但我無法弄清楚......
這是我的代碼:
切片作業.h
#pragma once
#include <future>
using namespace ::std;
class sliceWork
{
int sliceIntervalMilliSeconds;
int failureCounter;
int maxFailsBeforeRestart;
char* label = NULL;
promise<void> workPromise;
thread* workerThread = NULL;
virtual void init() = 0;
virtual bool oneSliceWork() = 0;
void work(future<void> future);
public:
sliceWork(int sliceInterval, int maxFails, const char* label);
~sliceWork();
void initWork();
void signalTerminate();
};
切片作業.cpp
#include <string.h>
#include "sliceWork.h"
sliceWork::sliceWork(int interval, int maxFails, const char* workLabel)
{
sliceIntervalMilliSeconds = interval;
maxFailsBeforeRestart = maxFails;
label = new char[strlen(workLabel) 1];
strcpy(label, workLabel);
}
sliceWork::~sliceWork()
{
if (workerThread != NULL && workerThread->joinable())
workerThread->join();
printf("destructor %s\n", label);
delete label;
delete workerThread;
}
void sliceWork::initWork()
{
failureCounter = 0;
init();
printf("Init work %s finished!\n", label);
future<void> futureWorker = workPromise.get_future();
workerThread = new thread(&sliceWork::work, this, move(futureWorker));
}
void sliceWork::work(future<void> future)
{
using namespace ::std::chrono;
steady_clock::time_point t0 = steady_clock::now();
while (future.wait_for(chrono::milliseconds(1)) == future_status::timeout)
{
if (duration_cast<chrono::milliseconds>(steady_clock::now() - t0).count()
> sliceIntervalMilliSeconds)
{
if (!oneSliceWork())
{
if ( failureCounter > maxFailsBeforeRestart
&& maxFailsBeforeRestart > 0)
{
init();
failureCounter = 0;
}
}
t0 = steady_clock::now();
}
}
printf("work terminated for %s!\n", label);
}
void sliceWork::signalTerminate()
{
printf("request terminate for work %s...\n", label);
workPromise.set_value();
}
這是一個按預期作業的示例:
主檔案
#include <string.h>
#include "sliceWork.h"
class A : public sliceWork
{
void init() {
printf("Init A...\n");
}
bool oneSliceWork() {
printf("Working A...\n");
return true;
}
public:
A(int slice, int max, const char* label)
: sliceWork(slice, max, label)
{
}
};
class B : public sliceWork
{
void init() {
printf("Init B...\n");
}
bool oneSliceWork() {
printf("Working B...\n");
return true;
}
public:
B(int slice, int max, const char* label)
: sliceWork(slice, max, label)
{
}
};
class C : public sliceWork
{
void init() {
printf("Init C...\n");
}
bool oneSliceWork() {
printf("Working C...\n");
return false;
}
public:
C(int slice, int max, const char* label)
: sliceWork(slice, max, label)
{
}
};
int main()
{
{
A a(1000, 1000, "A");
a.initWork();
B b(2000, 1000, "B" );
b.initWork();
C c(700, 2, "C" );
c.initWork();
printf("Initializations finished!\n");
::std::this_thread::sleep_for(::std::chrono::seconds(7));
a.signalTerminate();
::std::this_thread::sleep_for(::std::chrono::seconds(5));
b.signalTerminate();
::std::this_thread::sleep_for(::std::chrono::seconds(4));
c.signalTerminate();
}
getchar();
return 0;
}
所以,我想問一下這種方法是否容易出錯,因為我實作功能的方式。
應用程式是用 C 11 撰寫的,并針對運行 Raspberry 的 Debian 11 (Raspbian) 風格的 Raspberry PI 3b ,如果相關的話。
uj5u.com熱心網友回復:
從 C 11 開始,我們使用關鍵字nullptr而不是NULL宏。此外,std::thread它是可移動的,因此最好將其用作值而不是指標:
class sliceWork{
///...
std::thread workerThread;
///...
~sliceWork(){
///...
if (workerThread.joinable())
workerThread.join();
///...
};
///...
void initWork(){
///...
workerThread = thread{[this](){
work(workPromise.get_future());
}};
///...
};
};
我使用 lambda 而不是您的原始代碼來初始化執行緒;它具有更好的最低性能,同時更具可讀性 IMO。
如果您可以使用 C 17,那么我強烈建議您使用std::string_view舊的以空字符結尾的字串;否則只需使用std::string. 還始終建議使用建構式成員初始化器串列:
#include <string>
class sliceWork{
///...
std::string_view label;
///...
sliceWork(int interval, int maxFails, std::string_view workLabel):
sliceIntervalMilliSeconds {interval},
maxFailsBeforeRestart {maxFails},
label {workLabel}
{};
///...
};
但是,如果您可以使用 C 20,std::jthread則比std::thread. 因為現在你已經不自動delete加入labelorworkerThread和解構式了std::jthread,你可以完全放棄sliceWork;的解構式。default編譯器提供的解構式會做!另外,您甚至可以擺脫workPromise:
class sliceWork{
///...
std::string_view label;
///...
std::jthread workerThread;
///...
//std::promise<void> workPromise;//we don't need this
///...
//~sliceWork()=default;
///...
void signalTerminate(){
///...
workerThread.request_stop();
}
///...
void initWork(){
///...
workerThread = jthread{[this](std::stop_token stop_token){
work(std::move(stop_token));
}};
///...
};
///...
void work(std::stop_token stoken){
///...
for(int ticks{0}; !stoken.stop_requested(); sleep_for(chrono::milliseconds(1)), ticks) {
if (ticks > sliceIntervalMilliSeconds) {
///...
}; //if
///...
}; //for
///...
};//work
};
最后一句話:幾乎從不使用printf; 它有很多警告。在 C 中,我們使用std::cout. 在多執行緒應用程式中,以;結束std::cout指令 << std::endl這會重繪 緩沖區并幫助輸出更具可讀性。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/464686.html
上一篇:gmock模板類“constT1”:請求對齊為64的實際引數將不會對齊
下一篇:臨時初始化和參考初始化
