【发布时间】:2020-12-10 19:51:34
【问题描述】:
我目前正在为某些 SDR 编写 EXT_IO dll。但是,我遇到了一些问题,即 SDR 软件(我使用过 HDSDR 和 SDRSharper)在我创建了一个线程(从我的前端获取样本的消费者线程)后捕获了访问冲突异常。即使我创建的线程指向一个空函数(仅返回 0 的函数),我也会遇到这些访问冲突。我已经尝试过了,因为我认为我可以同时访问缓冲区。
有时 SDR 软件不会捕获异常而只是崩溃,我可以在 windows 事件观察器中看到它由于访问冲突而崩溃。
线程在一个单独的 C++ 类中,管理与我的前端的所有交互,并由作为 Ext_IO 接口的外部 C 部分调用(例如定义的 in this document )。
这是在我的 C++ 类中创建线程的代码
int startReception()
{
try
{
PLOGV_(PLOG_ID) << "Starting Sample Reception";
_Consumerthread = _beginthreadex(0, 0, &ArcaleRfTCPServer::receiveThread, this, 0, NULL);
if (_Consumerthread == NULL) return -1;
return 0;
}
catch (const std::exception& ex)
{
PLOGE_(PLOG_ID) << ex.what();
}
}
private :
static unsigned int __stdcall receiveThread(void* p_this)
{
ArcaleRfTCPServer* p_ArcaleRfTCPServer = static_cast<ArcaleRfTCPServer*>(p_this);
p_ArcaleRfTCPServer->recieve(); // Non-static member function!
return 0;
}
这是线程本身
void recieve()
{
try
{
PLOGV_(PLOG_ID) << "Starting Reciever Thread";
_rawData = (char*)calloc(_packetByteSize, sizeof(uint8_t));
if (_rawData == nullptr)
{
throw exception("Memory allocation for reception buffer failed");
}
PLOGV_(PLOG_ID) << "Memory Allocation was OK";
while (_active)
{
int bytesRead = 0;
int index = 0;
int bytesleft = _packetByteSize;
PLOGV_(PLOG_ID) << "SuiteRF trying to get lock";
lock.lock();
PLOGV_(PLOG_ID) << "Locked By SuiteRF";
while (bytesleft > 0) {
bytesRead = recieveMessage(_dataSock, &_rawData[index], bytesleft);
if (bytesRead <= 0) _endthreadex(-1);
bytesleft -= bytesRead;
index += bytesRead;
PLOGV_(PLOG_ID) << " read this run = " << bytesRead << "; left = " << bytesleft << "; total read = " << index;
}
lock.unlock();
PLOGV_(PLOG_ID) << "unlocked by SuiteRF";
_consumer(_rawData, index);
}
free(_rawData);
_rawData = nullptr;
_endthreadex(0);
}
catch (const std::exception& ex)
{
MessageBoxA(NULL, ex.what(), "Reader Thread Error", NULL);
free(_rawData);
_rawData = nullptr;
_endthreadex(-1);
}
}
消费者是在类外部定义的回调,在类构造函数中传递:
ArcaleRfTCPServer(int configPort, int DataPort, int timeout, void (*Consumer)(void* raw, int size))
它的代码是:
void __cdecl consumer(void* data, int count)
{
PLOGD << "Consumer IN";
int n, j;
try {
uint8_t* Raw = (uint8_t*)data;
SuiteRF->getLock();
for (n = 0, j = 0; n < count/2; n++, j++) {
PLOGV << "IQ[" << n << "] = Raw[" << j << "] + Raw[" << j + 1 << "] count is " << count;
iq[n] = (Raw[j] << 8) + Raw[++j];
}
SuiteRF->unlock();
callback(IQPair, 0, 0, &iq); // we tell the SDR software to get the sample here
PLOGD << "Consumer OUT";
}
catch (const exception& ex)
{
PLOGE << ex.what();
}
}
让我烦恼的是,访问冲突错误消息指向一个涉及 DLL 的异常,但是,我的所有代码都在 try-catch 块中,并且没有一个捕获异常。
另外,SDRSharper 倾向于告诉我,调用由我的 DLL 导出的函数时会出现异常,而该函数在创建线程之前已毫无问题地调用。
知道为什么吗?我看不到我在这里缺少什么。我很确定这很明显,但我完全可以理解。
感谢您的帮助
编辑:
为了更好地理解消费者中的iq 缓冲区是static volatile uint16_t,它分配的大小是_rawdata 的一半,因此J 在每个循环中递增两次(每个样本将 2 个 8 位组合成 1 个 16 位)
lock 是一个简单的mutex,但实际上它应该没有真正的用处,因为recieve() 和consumer() 是同步的,所以不会发生对原始缓冲区的并发访问,我快速实现只是为了确定。
我没有看到越界是这些访问冲突的根源,因为即使我在recieve() 中评论所有内容,我仍然会遇到这些访问冲突。另外,如果没有评论,访问冲突总是出现在j 或i 接近iq 或raw 的边界之前(可以肯定感谢上面的PLOGV 行)
我还尝试使用 std::thread 而不是当前方法创建线程,以摆脱 recieveThread() 中的指针,但结果相同
【问题讨论】:
-
不相关:考虑使用
std::vector而不是_rawData = (char*)calloc(_packetByteSize, sizeof(uint8_t));应该没有性能差异,因为优化器已经尝试过了,而且泄漏的可能性大大降低。 -
访问冲突是来自 C++ 领域之外的异常。操作系统、CPU 或低于程序的其他东西发现了错误。
try/catch无法捕捉到它。您需要使用信号处理程序。也就是说,一旦出现访问冲突,捕获它几乎总是毫无意义的。你所做的破坏系统的任何事情仍然潜伏着,并且可能正在造成你还没有看到的损害。 -
仅供参考 --
lock.lock(); ... lock.unlock();-- 无论lock是什么,如果receiveMessage返回<= 0,它将无法调用unlock。使用 RAII 和lock_guard或类似名称。 -
在
consumer中,您应该检查j的范围。看起来它每次迭代都会增加两次,如果索引Raw的有效范围可能会将其推出。 -
从技术上讲,这是一个中断,而不是异常。如果达到那个水平,就已经达到了操作系统的调试能力。可能的来源是越界或使用不正确的指针(本质上是相同的)。这些不保证会产生异常,它们会在 Windows 平台上造成中断
标签: c++ multithreading dll