EnterCriticalSection 死锁

2024-02-19

多线程日志记录应用程序出现死锁情况。

小背景:

我的主应用程序有 4-6 个线程正在运行。主线程负责监视我正在做的各种事情的运行状况、更新 GUI 等......然后我有一个传输线程和一个接收线程。发送和接收线程与物理硬件通信。我有时需要调试发送和接收线程看到的数据;即打印到控制台,而不会由于数据的时间紧迫性而中断它们。顺便说一句,数据位于 USB 总线上。

由于应用程序的线程性质,我想创建一个调试控制台,可以从其他线程向其发送消息。调试控制台作为低优先级线程运行,并实现环形缓冲区,以便当您打印到调试控制台时,消息会快速存储到环形缓冲区并设置和事件。调试控制台的线程根据传入的绑定消息来处理 WaitingOnSingleObject 事件。检测到事件时,控制台线程会使用该消息更新 GUI 显示。简单吧?打印调用和控制台线程使用关键部分来控制访问。

注意:如果我发现我正在丢弃消息,我可以调整环形缓冲区大小(至少是这样)。

在测试应用程序中,如果我通过鼠标单击缓慢调用其 Print 方法,控制台工作得非常好。我有一个按钮,可以按下它来向控制台发送消息,并且它可以工作。但是,如果我施加任何类型的负载(多次调用 Print 方法),一切都会死锁。当我跟踪死锁时,我的 IDE 调试器会跟踪 EnterCriticalSection 并驻留在那里。

注意:如果我删除 Lock/UnLock 调用并仅使用 Enter/LeaveCriticalSection(请参阅代码),我有时会工作,但仍然发现自己处于死锁情况。为了排除堆栈推送/弹出的死锁,我现在直接调用 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 调用次数加倍,我每次都很容易锁定......

另外,我更新的代码反映在上面。我知道确保我的设置和重置事件调用位于关键部分调用内。


关闭这些选项后,我会询问有关此“信息”对象的问题。它是一个窗口吗?它是哪个窗口的父窗口?它是在哪个线程上创建的?

如果 Info 或其父窗口是在另一个线程上创建的,则可能会出现以下情况:

控制台线程位于关键部分内,处理消息。 主线程调用 Print() 并阻塞在关键部分上,等待控制台线程释放锁。 Console线程调用Info(Caption)上的函数,这会导致系统sending向窗口发送消息(WM_SETTEXT)。 SendMessage 会阻塞,因为目标线程不处于消息可警报状态(在调用 GetMessage/WaitMessage/MsgWaitForMultipleObjects 时不会阻塞)。

现在你陷入了僵局。

每当您将阻塞例程与与窗口交互的任何内容混合在一起时,就会发生这种#$(%^。在 GUI 线程上使用的唯一合适的阻塞函数是 MSGWaitForMultipleObjects,否则对线程上托管的窗口的 SendMessage 调用很容易死锁。

避免这种情况涉及两种可能的方法:

  • 切勿在工作线程中进行任何 GUI 交互。仅使用 PostMessage 将非阻塞 UI 更新命令分派到 UI 线程,或者
  • 使用内核事件对象 + MSGWaitForMultipleObjects(在 GUI 线程上)来确保即使您阻塞某个资源,您仍然可以分派消息。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

EnterCriticalSection 死锁 的相关文章

随机推荐

  • Intellij IDEA 正则表达式字符类不能在字符范围内使用

    IDEA 不允许出现此错误 并且我无法找到关闭此类错误的选项 有谁知道如何修复错误或关闭警告 javascript 工作正常 只有 IDEA 认为这是一个问题 您正在使用以下命令创建一个范围连字符 在你的角色课程中 您应该将其移至任一端 另
  • 无符号 32 位整数的水平最小值和 SSE 中的位置

    我正在寻找一种方法来查找无符号 32 位整数的最小值及其在 SSE 中的位置 类似于 mm minpos epu16 我知道我可以通过一系列 mm min epu32 和洗牌 移位找到最小值 但这并不能让我找到位置 有人有任何很酷的方法吗
  • 使 SKScene 的背景透明不起作用...这是一个错误吗?

    有没有一种方法可以使 SKScene 的背景透明 并通过透明度将该场景呈现在另一个场景上 这个想法是让所呈现的场景的背景像这样 self backgroundColor SKColor colorWithRed 0 0f green 0 0
  • 将 HTML 表格粘贴到 Excel 中,如何在单元格中保留换行符

    我有一个简单的html表格 例如 只有一个单元格 但是当我复制dom节点并将其粘贴到excel中时 它将被识别为两行 如何使Excel获取正确的粘贴数据 table tr td 1 br 2 td tr tr tr table 我尝试添加c
  • Angular2 ngFor 跳过第一个索引[重复]

    这个问题在这里已经有答案了 如何跳过数组中的第一个索引 li user name is user age years old li 你可以使用片管 https angular io docs ts latest api common ind
  • 从 TableView 中的可编辑列动态填充 ChoiceBox

    基本上问题标题说明了一切 我有一列字符串 称为type 在表格视图和相应的数字列 称为size 每行代表一个对象CargoItem 它有两个属性type and size 两列都是可编辑的 TableView 与相应的可观察列表相关联Car
  • CFNetwork SSLHandshake 失败 iOS 9

    有使用 iOS 9 beta 1 的人遇到过这个问题吗 我使用标准 NSURLConnection 连接到 Web 服务 一旦调用 Web 服务 我就会收到以下错误 目前该功能适用 于 iOS 8 3 可能的测试版错误吗 任何想法或想法都会
  • 如何在 SonataAdmin 中创建自定义 DataGrid 过滤器

    我有一个带有许多状态代码的实体交易 我希望用户能够在 SonataAdmin 中将这些状态代码视为字符串 用户还应该能够根据这些状态代码进行过滤 Entity Transaction const TRANSACTION STATUS WAI
  • 有什么方法可以在 Phoenix 中定义自定义路线吗?

    假设我想创建一个resources通过添加一些自定义操作 rails 中的类似物是 resources tasks do member do get implement end end 这不仅会返回 7 条标准路线 还会返回 1 条新路线
  • 为什么我的本地通知在 iOS 10 中没有在前台触发?

    我试图理解为什么本地通知没有显示在前台 我相信我添加了所有正确的代码来允许此功能 但当我的应用程序位于前台时没有显示横幅 这是我添加的内容AppDelegate swift import UserNotifications UIApplic
  • 适用于 Android 的类似 iPhone 的导航点

    我必须创建一个像 iPhone 画廊一样带有导航点的图片库 查看屏幕截图 我需要这样一个小部件 看一下白点 Android 中是否有包含导航点的小部件 如果没有 我怎样才能重现这种效果 经过漫长的日子我发现从 Github 源查看寻呼机导航
  • 如何仅使用标准库将 UTC 日期时间转换为本地日期时间?

    我有一条蟒蛇datetime使用创建的实例datetime utcnow 并保存在数据库中 为了显示 我想转换datetime从数据库检索实例到本地datetime使用默认的本地时区 即 就好像datetime是使用创建的datetime
  • 在 SQL Server 数据库中自动添加对象及其关系

    假设我想插入一个新的Experiment在我的 SQL Server 数据库中 使用实体框架 4 0 Experiment有 1 Tasks in it Both Experiment and Task从获得EntityObject 此外
  • Javascript 数据表中的自动换行列数据

    我有一个 JS 数据表 我们在其中输入客户信息 在某些情况下 一些客户参考是这样的 reference text 26reference 5Ftext 3D 2526reference 255Ftext 253Dtest 252520ips
  • AudioKit v4.2 上的 AKRhodesPiano 错误(粉碎)

    我在用着AudioKit 版本 4 2 https github com AudioKit AudioKit releases tag v4 2在我的手机 iPhone X 上安装我的应用程序后 我收到此错误 参见附图 使用 AKRhode
  • 如何获取 SQL 中具有 MAX 和 MIN 值的行的 ID

    我正在努力使我的网站使用的查询更加高效 由于对 SQL 有点模糊 我还没有真正学会如何使用嵌套查询 但我刚刚设法得到了一些非常接近我想要的东西 我销售吉他 我有一个大数据库 其中单独列出了具有不同饰面选项的所有产品 项目在 dB 中具有唯一
  • oracle多用户功能

    如何创建可供不同用户使用的功能 它必须像内置的 oracle 函数一样工作 可以从任何用户调用 我想这可以通过授予一些特权来实现 这应该为特定用户执行此操作 GRANT EXECUTE ON your function TO some us
  • 如何将包含冒号的 GET 请求发送到 ASP.NET MVC2 控制器?

    这工作正常 GET mvc Movies TitleIncludes Lara 20Croft 当我提交包含冒号的请求时 如下所示 GET mvc Movies TitleIncludes Lara 20Croft 20Tomb 它会生成
  • 如何在Android Studio中更改项目名称

    将 eclipse 项目导入 Android Studio 并将其保存在新文件夹中 将包名称和所有内容更改为新名称 但项目名称仍然是旧项目中的名称 如何将指向的名称更改为其他名称 尝试更改此文件的内容 C Path To Project i
  • EnterCriticalSection 死锁

    多线程日志记录应用程序出现死锁情况 小背景 我的主应用程序有 4 6 个线程正在运行 主线程负责监视我正在做的各种事情的运行状况 更新 GUI 等 然后我有一个传输线程和一个接收线程 发送和接收线程与物理硬件通信 我有时需要调试发送和接收线