尝试将线程创建方法从 C 迁移到 C++ 不起作用
Trying to migrate thread creation method from C to C++ does not work
我一直在寻找具体信息来解决我的问题,但我认为它太具体了。我正在从事一个项目,该项目以一种非常混乱的方式混合了 C 和 C++ 代码。最后我不得不拿一些 C 代码并在用 C++ 编译的文件中使用它。我虽然它可以进行一些更改,但我发现了一个我无法自己解决的错误。这是关于线程的使用。
我已经声明了两个函数 public,因为我收到了它们,在我的头文件 drviRIOAD_1D.h
中声明了它们,在我的 class drviRIOAD_1D
:
void ai_pv_thread(void *p);
void aiDMA_thread(void *p);
然后我将其代码复制到我的 cpp 文件中,drviRIOAD_1D.cpp:
(我将只包括有错误的部分代码)
void drviRIOAD_1D::aiDMA_thread(void *p){
ai_pv_publish= (irio_dmathread_t*) malloc(sizeof(irio_dmathread_t)*irioPvt->DMATtoHOSTNCh[ai_dma_thread->id]);
buffersize=irioPvt->DMATtoHOSTBlockNWords[ai_dma_thread->id];
samples_per_channel=irioPvt->DMATtoHOSTBlockNWords[ai_dma_thread->id]*8; //Bytes per block
samples_per_channel= samples_per_channel/irioPvt->DMATtoHOSTSampleSize[ai_dma_thread->id]; //Samples per block
samples_per_channel= samples_per_channel/irioPvt->DMATtoHOSTNCh[ai_dma_thread->id];//Samples per channel per block
// Ring Buffers for Waveforms PVs
ai_dma_thread->IdRing= (void**) malloc(sizeof(epicsRingBytesId)*irioPvt->DMATtoHOSTNCh[ai_dma_thread->id]);
// Creation and Launching of threads working as consumers for EPICS PVs publishing
aux=(float**) malloc(sizeof(float*)*irioPvt->DMATtoHOSTNCh[ai_dma_thread->id]);
for(i=0;i<irioPvt->DMATtoHOSTNCh[ai_dma_thread->id];i++){
aux[i]=(float*) malloc(sizeof(float)*samples_per_channel);
if(ch_nelm[chIndex+i]!=0){
ai_dma_thread->IdRing[i]=epicsRingBytesCreate(samples_per_channel*irioPvt->DMATtoHOSTSampleSize[ai_dma_thread->id]*4096);//!<Ring buffer to store manage the waveforms.
ai_pv_publish[i].IdRing=&ai_dma_thread->IdRing[i];
ai_pv_publish[i].dma_thread_name=(char *)malloc(40);
sprintf(ai_pv_publish[i].dma_thread_name,"%sPVPublisher%02d",ai_dma_thread->dma_thread_name,i);
ai_pv_publish[i].id=i; //channel identifier
ai_pv_publish[i].dmanumber=ai_dma_thread->id; //dma identifier
ai_pv_publish[i].threadends=0;
ai_pv_publish[i].endAck=0;
ai_pv_publish[i].asynPvt=ai_dma_thread->asynPvt;
ai_pv_publish[i].dma_thread_id=epicsThreadCreate(ai_pv_publish[i].dma_thread_name,
epicsThreadPriorityHigh,epicsThreadGetStackSize(epicsThreadStackBig),
(EPICSTHREADFUNC)ai_pv_thread,
(void *)&ai_pv_publish[i]); //Here occurs the error, it means that ai_pv_thread argument does not work properly
}
最后一行给我一个错误,如下:
../drviRIOAD_1D.cpp:1846: error: invalid use of member (did you forget the ‘&’ ?)
这是函数 ai_pv_thread(void *p)
void drviRIOAD_1D::ai_pv_thread(void *p){
//There is one thread per ringbuffer (per DMA channel)
irio_dmathread_t *pv_thread;
pv_thread=(irio_dmathread_t *)p;
irioDrv_t *irioPvt = &pv_thread->asynPvt->drvPvt;
float* pv_data;
int pv_nelem=4096,aux;
while (irio_threadsrun==0) {usleep(10000);}
aux=irioPvt->DMATtoHOSTChIndex[pv_thread->dmanumber]+pv_thread->id;
printf ("ch_nelm %d\n", ch_nelm[aux]);
pv_nelem=ch_nelm[aux];
pv_data = (float*) malloc(sizeof(float)*pv_nelem);
do{
int NbytesDecimated;
NbytesDecimated=epicsRingBytesUsedBytes(*pv_thread->IdRing);
if(NbytesDecimated>=(sizeof(float)*pv_nelem))
{
if(epicsRingBytesIsFull(*pv_thread->IdRing)){
//TODO: Error
}
epicsRingBytesGet(*pv_thread->IdRing,(char*)pv_data,sizeof(float)*pv_nelem);
CallAIInsFloat32Array(pv_thread->asynPvt,CH,
irioPvt->DMATtoHOSTChIndex[pv_thread->dmanumber]+pv_thread->id,pv_data,pv_nelem);
}else{
//@todo: fix this
usleep(10000);
}
}while (pv_thread->threadends==0);
free(pv_data);
free(pv_thread->dma_thread_name);
pv_thread->endAck=1;
}
我不明白为什么这会给出任何错误,因为这段代码已经在工作了。我认为这是因为迁移,但我无法解决它。
事实上,我有一个接近 cpp 的例子,我一直在使用它,但没有结果。在这种情况下,他们创建了带有静态回调的线程,该回调位于 class 之外,调用 class 内的函数。区别在于 class 中函数的参数是 void
, void callbackTask()
而在我的例子中是 void ai_pv_thread(void *p)
.
给我出问题的函数是来自外部的API,它给出了一个C接口和C++接口,也许,这也是一个问题吗?我想我没有正确访问我的函数 ai_pv_thread,它是在没有参数的情况下调用的,但实际上它需要一个 void *p。但是我尝试了一些更改,例如添加 '::' (EPICSTHREADFUNC)::ai_pv_thread 但在那种情况下,编译器告诉我错误:'::ai_pv_thread' 未声明。
有人可以解释一下 C 和 C++ 中回调的区别,以便我解释这种行为吗?我必须做出什么改变才能使其正常工作?我可以更改函数参数,例如 static 或函数的位置,现在它们是 public 从 class drviRIOAD_1D,但我可以将它们取出来。
这是 link to this API(只是为了了解函数 epicsThreadCreate 的 api):
epicsThreadCreate
--> 创建一个新线程。优先级和 stackSize 参数的使用取决于实现。一些实现可能会忽略其中的一个或另一个,但为了可移植性,应该为两者提供适当的值。作为 stackSize 参数传递的值应通过调用 epicsThreadGetStackSize
获得。 funptr 参数指定一个实现线程的函数,parm 是传递给 funptr 的单个参数。当 funptr returns.
时线程终止
它的界面:
epicsThreadId epicsThreadCreate(const char *name,
unsigned int priority, unsigned int stackSize,
EPICSTHREADFUNC funptr,void *parm);
假设 ai_pv_thread(void*)
是一个成员函数,那么你所做的是未定义的行为。鉴于您遇到的错误,我倾向于这样认为。 start a thread using a member function you need to use a helper 这样:
static void pv_thread_start(void* in)
{
if(!in)
{
return;
}
std::unique_ptr<data_holder> holder(static_cast<data_holder*>(in));
holder->obj->ai_pv_thread(holder->data);
}
或者,您可以使用静态成员函数,但它的语法很奇怪,我通常会避免使用它。
创建线程时,您可能需要传入一个 POD 对象,该对象同时具有 *this
和您需要的数据,如下所示:
struct data_holder{
drviRIOAD_1D* obj;
ai_pv_publish* data;
};
在创建线程时:
auto data = std::make_unique<data_holder>({this, &ai_pv_publish[i]});
epicsThreadCreate(
ai_pv_publish[i].dma_thread_name,
epicsThreadPriorityHigh,
epicsThreadGetStackSize(epicsThreadStackBig),
// this cast is probably unncessary now
static_cast<EPICSTHREADFUNC>(pv_thread_start),
static_cast<void*>(data.release());
我一直在寻找具体信息来解决我的问题,但我认为它太具体了。我正在从事一个项目,该项目以一种非常混乱的方式混合了 C 和 C++ 代码。最后我不得不拿一些 C 代码并在用 C++ 编译的文件中使用它。我虽然它可以进行一些更改,但我发现了一个我无法自己解决的错误。这是关于线程的使用。
我已经声明了两个函数 public,因为我收到了它们,在我的头文件 drviRIOAD_1D.h
中声明了它们,在我的 class drviRIOAD_1D
:
void ai_pv_thread(void *p);
void aiDMA_thread(void *p);
然后我将其代码复制到我的 cpp 文件中,drviRIOAD_1D.cpp: (我将只包括有错误的部分代码)
void drviRIOAD_1D::aiDMA_thread(void *p){
ai_pv_publish= (irio_dmathread_t*) malloc(sizeof(irio_dmathread_t)*irioPvt->DMATtoHOSTNCh[ai_dma_thread->id]);
buffersize=irioPvt->DMATtoHOSTBlockNWords[ai_dma_thread->id];
samples_per_channel=irioPvt->DMATtoHOSTBlockNWords[ai_dma_thread->id]*8; //Bytes per block
samples_per_channel= samples_per_channel/irioPvt->DMATtoHOSTSampleSize[ai_dma_thread->id]; //Samples per block
samples_per_channel= samples_per_channel/irioPvt->DMATtoHOSTNCh[ai_dma_thread->id];//Samples per channel per block
// Ring Buffers for Waveforms PVs
ai_dma_thread->IdRing= (void**) malloc(sizeof(epicsRingBytesId)*irioPvt->DMATtoHOSTNCh[ai_dma_thread->id]);
// Creation and Launching of threads working as consumers for EPICS PVs publishing
aux=(float**) malloc(sizeof(float*)*irioPvt->DMATtoHOSTNCh[ai_dma_thread->id]);
for(i=0;i<irioPvt->DMATtoHOSTNCh[ai_dma_thread->id];i++){
aux[i]=(float*) malloc(sizeof(float)*samples_per_channel);
if(ch_nelm[chIndex+i]!=0){
ai_dma_thread->IdRing[i]=epicsRingBytesCreate(samples_per_channel*irioPvt->DMATtoHOSTSampleSize[ai_dma_thread->id]*4096);//!<Ring buffer to store manage the waveforms.
ai_pv_publish[i].IdRing=&ai_dma_thread->IdRing[i];
ai_pv_publish[i].dma_thread_name=(char *)malloc(40);
sprintf(ai_pv_publish[i].dma_thread_name,"%sPVPublisher%02d",ai_dma_thread->dma_thread_name,i);
ai_pv_publish[i].id=i; //channel identifier
ai_pv_publish[i].dmanumber=ai_dma_thread->id; //dma identifier
ai_pv_publish[i].threadends=0;
ai_pv_publish[i].endAck=0;
ai_pv_publish[i].asynPvt=ai_dma_thread->asynPvt;
ai_pv_publish[i].dma_thread_id=epicsThreadCreate(ai_pv_publish[i].dma_thread_name,
epicsThreadPriorityHigh,epicsThreadGetStackSize(epicsThreadStackBig),
(EPICSTHREADFUNC)ai_pv_thread,
(void *)&ai_pv_publish[i]); //Here occurs the error, it means that ai_pv_thread argument does not work properly
}
最后一行给我一个错误,如下:
../drviRIOAD_1D.cpp:1846: error: invalid use of member (did you forget the ‘&’ ?)
这是函数 ai_pv_thread(void *p)
void drviRIOAD_1D::ai_pv_thread(void *p){
//There is one thread per ringbuffer (per DMA channel)
irio_dmathread_t *pv_thread;
pv_thread=(irio_dmathread_t *)p;
irioDrv_t *irioPvt = &pv_thread->asynPvt->drvPvt;
float* pv_data;
int pv_nelem=4096,aux;
while (irio_threadsrun==0) {usleep(10000);}
aux=irioPvt->DMATtoHOSTChIndex[pv_thread->dmanumber]+pv_thread->id;
printf ("ch_nelm %d\n", ch_nelm[aux]);
pv_nelem=ch_nelm[aux];
pv_data = (float*) malloc(sizeof(float)*pv_nelem);
do{
int NbytesDecimated;
NbytesDecimated=epicsRingBytesUsedBytes(*pv_thread->IdRing);
if(NbytesDecimated>=(sizeof(float)*pv_nelem))
{
if(epicsRingBytesIsFull(*pv_thread->IdRing)){
//TODO: Error
}
epicsRingBytesGet(*pv_thread->IdRing,(char*)pv_data,sizeof(float)*pv_nelem);
CallAIInsFloat32Array(pv_thread->asynPvt,CH,
irioPvt->DMATtoHOSTChIndex[pv_thread->dmanumber]+pv_thread->id,pv_data,pv_nelem);
}else{
//@todo: fix this
usleep(10000);
}
}while (pv_thread->threadends==0);
free(pv_data);
free(pv_thread->dma_thread_name);
pv_thread->endAck=1;
}
我不明白为什么这会给出任何错误,因为这段代码已经在工作了。我认为这是因为迁移,但我无法解决它。
事实上,我有一个接近 cpp 的例子,我一直在使用它,但没有结果。在这种情况下,他们创建了带有静态回调的线程,该回调位于 class 之外,调用 class 内的函数。区别在于 class 中函数的参数是 void
, void callbackTask()
而在我的例子中是 void ai_pv_thread(void *p)
.
给我出问题的函数是来自外部的API,它给出了一个C接口和C++接口,也许,这也是一个问题吗?我想我没有正确访问我的函数 ai_pv_thread,它是在没有参数的情况下调用的,但实际上它需要一个 void *p。但是我尝试了一些更改,例如添加 '::' (EPICSTHREADFUNC)::ai_pv_thread 但在那种情况下,编译器告诉我错误:'::ai_pv_thread' 未声明。
有人可以解释一下 C 和 C++ 中回调的区别,以便我解释这种行为吗?我必须做出什么改变才能使其正常工作?我可以更改函数参数,例如 static 或函数的位置,现在它们是 public 从 class drviRIOAD_1D,但我可以将它们取出来。
这是 link to this API(只是为了了解函数 epicsThreadCreate 的 api):
epicsThreadCreate
--> 创建一个新线程。优先级和 stackSize 参数的使用取决于实现。一些实现可能会忽略其中的一个或另一个,但为了可移植性,应该为两者提供适当的值。作为 stackSize 参数传递的值应通过调用 epicsThreadGetStackSize
获得。 funptr 参数指定一个实现线程的函数,parm 是传递给 funptr 的单个参数。当 funptr returns.
它的界面:
epicsThreadId epicsThreadCreate(const char *name,
unsigned int priority, unsigned int stackSize,
EPICSTHREADFUNC funptr,void *parm);
假设 ai_pv_thread(void*)
是一个成员函数,那么你所做的是未定义的行为。鉴于您遇到的错误,我倾向于这样认为。 start a thread using a member function you need to use a helper 这样:
static void pv_thread_start(void* in)
{
if(!in)
{
return;
}
std::unique_ptr<data_holder> holder(static_cast<data_holder*>(in));
holder->obj->ai_pv_thread(holder->data);
}
或者,您可以使用静态成员函数,但它的语法很奇怪,我通常会避免使用它。
创建线程时,您可能需要传入一个 POD 对象,该对象同时具有 *this
和您需要的数据,如下所示:
struct data_holder{
drviRIOAD_1D* obj;
ai_pv_publish* data;
};
在创建线程时:
auto data = std::make_unique<data_holder>({this, &ai_pv_publish[i]});
epicsThreadCreate(
ai_pv_publish[i].dma_thread_name,
epicsThreadPriorityHigh,
epicsThreadGetStackSize(epicsThreadStackBig),
// this cast is probably unncessary now
static_cast<EPICSTHREADFUNC>(pv_thread_start),
static_cast<void*>(data.release());