【问题标题】:Access violation on C/C++ DLL caller after thread creation线程创建后 C/C++ DLL 调用者的访问冲突
【发布时间】: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() 中评论所有内容,我仍然会遇到这些访问冲突。另外,如果没有评论,访问冲突总是出现在ji 接近iqraw 的边界之前(可以肯定感谢上面的PLOGV 行)

我还尝试使用 std::thread 而不是当前方法创建线程,以摆脱 recieveThread() 中的指针,但结果相同

【问题讨论】:

  • 不相关:考虑使用 std::vector 而不是 _rawData = (char*)calloc(_packetByteSize, sizeof(uint8_t)); 应该没有性能差异,因为优化器已经尝试过了,而且泄漏的可能性大大降低。
  • 访问冲突是来自 C++ 领域之外的异常。操作系统、CPU 或低于程序的其他东西发现了错误。 try/catch 无法捕捉到它。您需要使用信号处理程序。也就是说,一旦出现访问冲突,捕获它几乎总是毫无意义的。你所做的破坏系统的任何事情仍然潜伏着,并且可能正在造成你还没有看到的损害。
  • 仅供参考 -- lock.lock(); ... lock.unlock(); -- 无论lock 是什么,如果receiveMessage 返回&lt;= 0 ,它将无法调用unlock。使用 RAII 和 lock_guard 或类似名称。
  • consumer 中,您应该检查j 的范围。看起来它每次迭代都会增加两次,如果索引Raw 的有效范围可能会将其推出。
  • 从技术上讲,这是一个中断,而不是异常。如果达到那个水平,就已经达到了操作系统的调试能力。可能的来源是越界或使用不正确的指针(本质上是相同的)。这些不保证会产生异常,它们会在 Windows 平台上造成中断

标签: c++ multithreading dll


【解决方案1】:

答案是:PLOG。

如您所见,我运行了 2 个 plog 实例。一个具有默认实例编号,在我的班级之外,第二个在我的班级内部具有实例编号 PLOG_ID(带有 #DEFINE PLOG_ID 42 )。

我删除了所有引用并在课堂外调用了 plog,只保留了课堂内的 PLOGx_(PLOG_ID) 宏和 BOOM!没有更多的例外。

或者当应该提供帮助的工具是导致问题的工具时......

还是谢谢大家

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-03-19
    • 1970-01-01
    • 1970-01-01
    • 2011-09-21
    • 1970-01-01
    • 2010-11-19
    • 1970-01-01
    • 2012-09-12
    相关资源
    最近更新 更多