- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
多线程日志记录应用程序出现死锁情况。
小背景:
我的主应用程序有 4-6 个线程在运行。主线程负责监视我正在做的各种事情的健康状况、更新 GUI 等……然后我有一个传输线程和一个接收线程。传输和接收线程与物理硬件对话。有时我需要调试发送和接收线程看到的数据;即打印到控制台,而不会由于数据的时间关键性而中断它们。顺便说一句,数据在 USB 总线上。
由于应用程序的线程特性,我想创建一个调试控制台,我可以从其他线程向其发送消息。调试控制台作为低优先级线程运行并实现环形缓冲区,这样当您打印到调试控制台时,消息会快速存储到环形缓冲区并设置和事件。调试控制台的线程从传入的绑定(bind)消息中获取 WaitingOnSingleObject 事件。当检测到事件时,控制台线程使用消息更新 GUI 显示。简单吧?打印调用和控制台线程使用临界区来控制访问。
注意:如果我发现我正在丢弃消息,我可以调整环形缓冲区大小(至少我是这么想的)。
在测试应用程序中,如果我通过鼠标点击缓慢地调用它的 Print 方法,控制台工作得很好。我有一个按钮,我可以按下该按钮将消息发送到控制台并且它可以工作。但是,如果我施加任何负载(多次调用 Print 方法),一切都会死锁。当我跟踪死锁时,我的 IDE 的调试器跟踪到 EnterCriticalSection 并停在那里。
注意:如果我删除 Lock/UnLock 调用并仅使用 Enter/LeaveCriticalSection(参见代码),我有时会工作,但仍然发现自己处于死锁状态。为了排除堆栈 push/pops 的死锁,我现在直接调用 Enter/LeaveCriticalSection 但这并没有解决我的问题....这是怎么回事?
这是一个 Print 语句,它允许我将一个简单的 int 传递给显示控制台。
void TGDB::Print(int I)
{
//Lock();
EnterCriticalSection(&CS);
if( !SuppressOutput )
{
//swprintf( MsgRec->Msg, L"%d", I);
sprintf( MsgRec->Msg, "%d", I);
MBuffer->PutMsg(MsgRec, 1);
}
SetEvent( m_hEvent );
LeaveCriticalSection(&CS);
//UnLock();
}
// My Lock/UnLock methods
void TGDB::Lock(void)
{
EnterCriticalSection(&CS);
}
bool TGDB::TryLock(void)
{
return( TryEnterCriticalSection(&CS) );
}
void TGDB::UnLock(void)
{
LeaveCriticalSection(&CS);
}
// This is how I implemented Console's thread routines
DWORD WINAPI TGDB::ConsoleThread(PVOID pA)
{
DWORD rVal;
TGDB *g = (TGDB *)pA;
return( g->ProcessMessages() );
}
DWORD TGDB::ProcessMessages()
{
DWORD rVal;
bool brVal;
int MsgCnt;
do
{
rVal = WaitForMultipleObjects(1, &m_hEvent, true, iWaitTime);
switch(rVal)
{
case WAIT_OBJECT_0:
EnterCriticalSection(&CS);
//Lock();
if( KeepRunning )
{
Info->Caption = "Rx";
Info->Refresh();
MsgCnt = MBuffer->GetMsgCount();
for(int i=0; i<MsgCnt; i++)
{
MBuffer->GetMsg( MsgRec, 1);
Log->Lines->Add(MsgRec->Msg);
}
}
brVal = KeepRunning;
ResetEvent( m_hEvent );
LeaveCriticalSection(&CS);
//UnLock();
break;
case WAIT_TIMEOUT:
EnterCriticalSection(&CS);
//Lock();
Info->Caption = "Idle";
Info->Refresh();
brVal = KeepRunning;
ResetEvent( m_hEvent );
LeaveCriticalSection(&CS);
//UnLock();
break;
case WAIT_FAILED:
EnterCriticalSection(&CS);
//Lock();
brVal = false;
Info->Caption = "ERROR";
Info->Refresh();
aLine.sprintf("Console error: [%d]", GetLastError() );
Log->Lines->Add(aLine);
aLine = "";
LeaveCriticalSection(&CS);
//UnLock();
break;
}
}while( brVal );
return( rVal );
}
MyTest1 和 MyTest2 只是我响应按钮按下而调用的两个测试函数。无论我点击按钮的速度有多快,MyTest1 都不会出现问题。 MyTest2 几乎每次都死锁。
// No Dead Lock
void TTest::MyTest1()
{
if(gdb)
{
// else where: gdb = new TGDB;
gdb->Print(++I);
}
}
// Causes a Dead Lock
void TTest::MyTest2()
{
if(gdb)
{
// else where: gdb = new TGDB;
gdb->Print(++I);
gdb->Print(++I);
gdb->Print(++I);
gdb->Print(++I);
gdb->Print(++I);
gdb->Print(++I);
gdb->Print(++I);
gdb->Print(++I);
}
}
更新:在我的环形缓冲区实现中发现了一个错误。在重负载下,当缓冲区包装时,我没有正确检测到完整的缓冲区,因此缓冲区没有返回。我很确定这个问题现在已经解决了。一旦我解决了环形缓冲区问题,性能就变得更好了。但是,如果我减少 iWaitTime,我的死锁(或卡住问题)就会返回。
因此,在使用更重的负载进行进一步测试后,我的死锁似乎并没有消失。在超重负载下,我继续死锁,或者至少我的应用程序卡住了,但自从我修复了环形缓冲区问题后,它就没有使用过。如果我将 MyTest2 中的 Print 调用次数加倍,我每次都可以轻松锁定....
此外,我更新的代码反射(reflect)在上面。我知道确保我的设置和重置事件调用在临界区调用内。
最佳答案
关闭这些选项后,我会询问有关此“信息”对象的问题。它是一个窗口,它是哪个窗口的父级,它是在哪个线程上创建的?
如果 Info 或其父窗口是在另一个线程上创建的,则可能会出现以下情况:
控制台线程在临界区内,处理消息。主线程调用 Print() 并在关键部分阻塞,等待控制台线程释放锁。Console 线程调用 Info (Caption) 函数,导致系统发送一条消息 (WM_SETTEXT) 到窗口。 SendMessage 阻塞,因为目标线程未处于消息可警报状态(未在调用 GetMessage/WaitMessage/MsgWaitForMultipleObjects 时阻塞)。
现在你有一个僵局。
只要您将阻塞例程与任何与窗口交互的东西混合使用,就会发生这种#$(%^。唯一适合在 GUI 线程上使用的阻塞函数是 MSGWaitForMultipleObjects,否则对线程上托管的窗口的 SendMessage 调用很容易死锁.
避免这种情况涉及两种可能的方法:
关于c++ - 进入临界区死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5042076/
#include using namespace std; class C{ private: int value; public: C(){ value = 0;
这个问题已经有答案了: What is the difference between char a[] = ?string?; and char *p = ?string?;? (8 个回答) 已关闭
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 7 年前。 此帖子已于 8 个月
除了调试之外,是否有任何针对 c、c++ 或 c# 的测试工具,其工作原理类似于将独立函数复制粘贴到某个文本框,然后在其他文本框中输入参数? 最佳答案 也许您会考虑单元测试。我推荐你谷歌测试和谷歌模拟
我想在第二台显示器中移动一个窗口 (HWND)。问题是我尝试了很多方法,例如将分辨率加倍或输入负值,但它永远无法将窗口放在我的第二台显示器上。 关于如何在 C/C++/c# 中执行此操作的任何线索 最
我正在寻找 C/C++/C## 中不同类型 DES 的现有实现。我的运行平台是Windows XP/Vista/7。 我正在尝试编写一个 C# 程序,它将使用 DES 算法进行加密和解密。我需要一些实
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
有没有办法强制将另一个 窗口置于顶部? 不是应用程序的窗口,而是另一个已经在系统上运行的窗口。 (Windows, C/C++/C#) 最佳答案 SetWindowPos(that_window_ha
假设您可以在 C/C++ 或 Csharp 之间做出选择,并且您打算在 Windows 和 Linux 服务器上运行同一服务器的多个实例,那么构建套接字服务器应用程序的最明智选择是什么? 最佳答案 如
你们能告诉我它们之间的区别吗? 顺便问一下,有什么叫C++库或C库的吗? 最佳答案 C++ 标准库 和 C 标准库 是 C++ 和 C 标准定义的库,提供给 C++ 和 C 程序使用。那是那些词的共同
下面的测试代码,我将输出信息放在注释中。我使用的是 gcc 4.8.5 和 Centos 7.2。 #include #include class C { public:
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我的客户将使用名为 annoucement 的结构/类与客户通信。我想我会用 C++ 编写服务器。会有很多不同的类继承annoucement。我的问题是通过网络将这些类发送给客户端 我想也许我应该使用
我在 C# 中有以下函数: public Matrix ConcatDescriptors(IList> descriptors) { int cols = descriptors[0].Co
我有一个项目要编写一个函数来对某些数据执行某些操作。我可以用 C/C++ 编写代码,但我不想与雇主共享该函数的代码。相反,我只想让他有权在他自己的代码中调用该函数。是否可以?我想到了这两种方法 - 在
我使用的是编写糟糕的第 3 方 (C/C++) Api。我从托管代码(C++/CLI)中使用它。有时会出现“访问冲突错误”。这使整个应用程序崩溃。我知道我无法处理这些错误[如果指针访问非法内存位置等,
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。 关闭 7 年前。
已关闭。此问题不符合Stack Overflow guidelines 。目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,因为
我有一些 C 代码,将使用 P/Invoke 从 C# 调用。我正在尝试为这个 C 函数定义一个 C# 等效项。 SomeData* DoSomething(); struct SomeData {
这个问题已经有答案了: Why are these constructs using pre and post-increment undefined behavior? (14 个回答) 已关闭 6
我是一名优秀的程序员,十分优秀!