尝试将线程创建方法从C迁移到C++不起作用

Trying to migrate thread creation method from C to C++ does not work

本文关键字:迁移 C++ 不起作用 方法 线程 创建      更新时间:2023-10-16

我一直在寻找具体的信息来解决我的问题,但我认为它太具体了。我正在做一个项目,它混合了c和c++代码,这真的很令人困惑。最后,我不得不使用一些C代码,并在用C++编译的文件中使用它。我认为它可以进行一些更改,但我发现了一个我自己无法解决的错误。这是关于线程的使用。

我已经声明了两个函数为public,因为我已经收到了它们,并在我的头文件drviRIOAD_1D.h中声明了它们,在我的类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:错误:无效使用成员(您忘记了"&"吗?)

这是函数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 %dn", 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的例子,我一直在使用它,但没有结果。在这种情况下,他们创建了具有静态回调的线程,该静态回调位于调用类内函数的类之外。不同的是,类内函数的参数是voidvoid callbackTask(),在我的情况下是void ai_pv_thread(void *p)

给我带来问题的函数来自一个外部API,它提供了一个C接口和C++接口,也许这也是一个问题吗?我认为我没有正确地访问函数ai_pv_thread,它是在没有参数的情况下调用的,但实际上它需要一个void*p。但是,我尝试了一些更改,例如添加"::"(EPICSTHREADFDUNC)::ai_pv_thread,但在这种情况下,编译器告诉我错误:"::ai_prv_thread'未声明。

有人能解释一下C和C++中回调的区别吗?这样我就可以解释这种行为了?为了让它发挥作用,我必须做出什么改变?我可以更改函数参数,如static或函数的位置,现在它们是drviRIOAD_1D类中的公共参数,但我可以删除它们。

这是这个API的链接(只想知道函数epicsThreadCreate的API):
epicsThreadCreate-->创建一个新线程。优先级和stackSize参数的使用取决于实现。一些实现可能会忽略其中的一个或另一个,但为了可移植性,应该为这两个提供适当的值。作为stackSize参数传递的值应该通过调用epicsThreadGetStackSize来获得。funptr参数指定了一个实现线程的函数,parm是传递给funptr的单个参数。当funptr返回时,线程终止。

它的接口:

epicsThreadId epicsThreadCreate(const char *name,
unsigned int priority, unsigned int stackSize,
EPICSTHREADFUNC funptr,void *parm);

假设ai_pv_thread(void*)是一个成员函数,那么您所做的是未定义的行为。考虑到你得到的错误,我倾向于这样认为。要使用成员函数启动线程,你需要使用这样的助手:

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());