时间问题?程序在 Visual Studio 中运行良好,但不是独立的

Posted

技术标签:

【中文标题】时间问题?程序在 Visual Studio 中运行良好,但不是独立的【英文标题】:Timing issue? Program runs fine in Visual Studio but not standalone 【发布时间】:2014-11-19 16:49:50 【问题描述】:

我有一个本地 C++ DLL。 DLL 创建一个从外部设备重复读取数据的线程。如果数据读取速度不够快,设备可能会溢出。

“读取”循环如下所示:

while (true)

    read_from_device();

    buffer_data();

    Sleep(5);  //Allow data to accumulate 

我还有一个使用上述 DLL 的 C# 应用程序。它调用 DLL 中的一个函数来启动“读取”线程,然后定期调用 DLL 中的另一个函数来检查是否已从外部设备读取所有数据。

应用程序和 DLL 在 Visual Studio(本例中为 2008)中的“调试”模式下构建。

如果我通过 Visual Studio 运行应用程序,一切都很好:从外部设备读取数据并且不会发生溢出。

当我“独立”运行同一个应用程序(通过直接运行 .exe)时,我遇到了溢出。

我认为唯一的区别是在第一个实例中,Visual Studio 调试器会自动附加到应用程序?请记住,我没有设置断点。

我的应用程序中一定存在时间问题,当调试器附加到它时,它以某种方式得到纠正?

我该如何调试这个问题?

通过 Visual Studio 运行应用程序与直接运行应用程序之间还有哪些区别?

编辑

下面是更详细的代码:

int buffer[MAX_EVENTS*2];
int bufferIndex = 0;
int eventsReturned = 0;

BufferObject *pNewBuffer;

while (bPollData)

    read_data(buffer[bufferIndex], &eventsReturned);

    bufferIndex += eventsReturned;

    if (bufferIndex >= MAX_EVENTS)
    
        pNewBuffer = new BufferObject(buffer, bufferIndex);

        myList.AddTail(pNewBuffer);

        bufferIndex = 0;
    

    Sleep(5);


...
... 

class BufferObject 
    int *buffer;
    int bufferSize;
public:
    BufferObject(int* source, int size)
    
        buffer = new int[size];
        bufferSize = size;
        memcpy(buffer, source, size);
    
;

【问题讨论】:

通常和内存有关。无论如何,我们需要查看更多代码。 您是否正在使用优化编译您的 DLL?您是否尝试过移除睡眠,即使它在连接到 VS 时工作正常? 检查 Windows 日志中是否存在导致崩溃的错误 你为什么要睡在那里?当然,如果你想避免溢出,你应该尽可能快地阅读。顺便说一句,你最好让你的读取循环检查手动重置事件,这样你就可以在完成后干净地关闭你的线程...... @LenHolgate 睡眠是为了让数据积累。如果我过于频繁地从设备读取数据,则设备会将所有时间都花在服务请求上,而不是收集数据。该线程确实有更好的关闭方式,我只是没有包含它。 【参考方案1】:
   Sleep(5);

嗯,这就是你问题的 99%。你已经知道当你的代码运行速度快时你会遇到问题。你人为地减慢了它的速度,以便在虫子上贴上创可贴。但是,当您执行使您的代码运行得更快的else 操作时,它再次中断当然不应该感到惊讶。就像不附加调试器一样运行它。

您可能甚至不知道 5 就足够了。这是一个相当随机的数字,您的程序实际上睡眠的时间很少恰好是 5 毫秒。默认睡眠精度为 15.625 毫秒,但当另一个程序调用 timeBeginPeriod() 时可能会改变。只需运行一个浏览器就足够了,众所周知,Chrome 会改变中断率。所以你突然跑得快了 3 倍,而不是 15 毫秒。破坏你的程序。您不希望 Chrome 破坏您的程序;)

你必须解决真正的问题。这肯定就像您只是将设备发送给您的多少字节都放入一个 BufferObject 中。因此,如果您的程序运行得很快,您将获得 许多 个缓冲区对象,每个缓冲区对象只有一点点数据。这可能会对使用数据的任何代码造成严重破坏。一个传统的问题是程序的 UI 变得紧张,疯狂地试图跟上所需的重绘。

如果没有从问题中获得足够的洞察力,随机猜测是您必须首先缓冲设备响应,直到它积累足够的字节以使其值得将其粘贴到 BufferObject 中。如果您继续使用 Sleep() 创可贴,则选择一个向下舍入的 15.625 整数倍的睡眠量。比如 15、31、46。

【讨论】:

就在这一刻,我正在记录调用 read_data() 函数之间的时间,并得到 156000us。我没有意识到这一点。如果我删除 Sleep(n),设备会花费所有时间来服务 read_data() 请求,这对其收集数据的其他(主要)任务产生不利影响。所以我需要以某种方式限制调用 read_data() 的频率。非常感谢您的回答,这可能会让我进步。 设备是否以恒定或可变速率收集?您可以尝试根据返回的数据点数动态调整延迟。因此,如果它从一次调用中返回超过 X,则您立即再次调用它,否则您等待...【参考方案2】:

您可能会遇到收集数据(并构建缓冲区列表)的线程与应用程序用于访问(并可能删除)缓冲区的线程之间缺乏同步的问题。

您可能需要在访问缓冲区列表时添加一些锁定。似乎您有一个“活动”缓冲区,只有数据读取器线程可以写入(缓冲区),当它已满时,您将此数据复制到一个新的缓冲区对象中并将其添加到列表中。您可能应该锁定列表的使用(该列表可能在内部包含正确的同步,但从您的代码示例中不清楚)。

循环似乎无法通知 read_data() 函数它传入的缓冲区中的剩余空间,这可能是导致内存溢出的原因。

就个人而言,因为无论如何您都在动态分配缓冲区对象,所以我会完全跳过内存副本,只需保存一个活动缓冲区对象并直接读入,而不是读入临时缓冲区,然后将其复制到缓冲区对象中。

鉴于问题的性质和代码的缺乏,除了猜测之外不可能做更多的事情。

【讨论】:

谢谢伦。该列表实际上永远不会同时访问,因此我不需要锁定对它的访问 - 但是我对此束手无策,所以无论如何我都会尝试一下。我还将再看一下代码的 read_data() 方面。你关于多余的内存副本的观点是一个很好的观点,我一定会做出改变。我很欣赏由于缺少代码而难以评估问题,但我最初的想法是代码实际上是合理的,只是调试器的存在与否改变了应用程序的时间。我没有考虑内存问题 @digital_fate:请注意,如果您的代码因调试器更改时间而失败,则代码正确。它应该独立于确切的时间来工作——Windows 不是一个实时操作系统,它不能给你任何保证。今天是调试器更改时间,明天是 Windows 更新。 Jeroen,或者,很可能,只是在另一台机器上运行它 :)

以上是关于时间问题?程序在 Visual Studio 中运行良好,但不是独立的的主要内容,如果未能解决你的问题,请参考以下文章

在 Visual Studio 2010 调试器中运行时不会显示 Windows 公用文件对话框

Windows 应用程序在 Visual Studio 2019 中运行时工作,在我在本地构建和运行后工作,但是当提交到应用商店并下载时,它不运行

System.NullReferenceException 用于在 Windows 服务器 IIS 上运行但在 Visual Studio IIS Express 中运行时在本地运行的 .NET 服务

微软正式发布Visual Studio Kubernetes工具包

如何在 Visual Studio For Mac 中执行 T4 模板文件?

Visual Studio 高级程序在 Visual Studio Express 中打开?