从 UI 调用 System.Threading.Thread 时锁挂起

2023-12-21

EDIT:请参阅问题历史记录,了解未更改的问题,以免评论无效。

我单击执行某些代码的按钮并创建一个线程(System.Threading.Thread)。当我重新单击启动进程的按钮时,它会挂起并冻结用户界面。可能是什么原因?

public partial class ucLoader : UserControl
{
    //lock object for whole instance of class ucLoader
    private object lockUcLoader = new object();

    //bringing info from ui
    private void btnBringInfo_Click(object sender, EventArgs e)
    {
        lock (lockUcLoader)
        {
            btnBringInfo_PerformClick(false);
        }
    }

    //using this method because it could be called when even button not visible
    internal void btnBringInfo_PerformClick(bool calledFromBandInit)
    {
        lock (lockUcLoader) //HANGS HERE when called multiple times and ui freeze as well
        //by the way I am using (repetitive) lock, because this method also called independently from btnBringInfo_Click
        {
            //...
            this.btnLoad_PerformClick();
        }
    }

    //Another button perform click that could be triggered elsewhere when even button not visible
    private void btnLoad_PerformClick()
    {
        lock (lockUcLoader) //I am using (repetitive) lock, because this method also called independently from btnBringInfo_PerformClick
        {
            //...
            Run();  
        }
    }

    //method for creating thread which System.Threading.Thread  
    private void Run()
    {
       lock (lockUcLoader)  //Maybe this lock is NOT REQUIRED, as it is called by only btnLoad_PerformClick(), could you please confirm?
        {
            //some code that thread can be killed when available,  you can ingore this two lines as they are irrelevant to subject, I think
            Source = new CancellationTokenSource();
            Token = Source.Token;
            var shell = new WindowsShell();
            Thread = new Thread((object o) =>
            {
                //...
                var tokenInThread = (CancellationToken)o;
                exitCode =TaskExtractBatchFiles(cls, shell, exitCode);


                 using (var logEnt = new logEntities())
                {

                        //Do some db operation
                        //...
                        this.Invoke((MethodInvoker)delegate
                        {
                            //do some ui update operation
                            //...
                        });
                    }
            }
            Thread.Start(Token);
        }      
    } 

    public void Progress(string message)
    {
        Invoke((MethodInvoker)delegate  //ATTENTION HERE see below picture Wait occurs here
        {
            if (message != null && message.Trim() != string.Empty)
            {
                this.txtStatus.AppendText(message + Environment.NewLine);
            }
        });
    }       
}       

为了避免被关闭的问题,我的问题是如何防止 可以在没有后台线程和 ui 线程锁定的情况下访问以下方法

public void Progress(string message)
        {
            Invoke((MethodInvoker)delegate  //ATTENTION HERE see below picture Wait occurs here
            {
                if (message != null && message.Trim() != string.Empty)
                {
                    this.txtStatus.AppendText(message + Environment.NewLine);
                }
            });
        }   

   Invoke((MethodInvoker)delegate ...

每当您使用lock在你的代码中声明,那么你总是冒着诱导的风险deadlock。经典的线程错误之一。通常,您至少需要两个锁才能到达那里,并且以错误的顺序获取它们。是的,你的程序中有两个。一个你自己宣称的。还有一个你看不到的,因为它埋在使 Control.Invoke() 工作的管道内部。无法看到锁使得死锁成为难以调试的问题。

您可以推断出,Control.Invoke 内部的锁是必要的,以确保工作线程被阻塞,直到 UI 线程执行委托目标。可能还有助于推理出程序陷入僵局的原因。您启动了工作线程,它获取了lockUcLoaderlock 并开始执行其工作,同时调用 Control.Invoke。现在,您在工作人员完成之前单击按钮,它必然会阻塞。但这会使 UI 线程陷入紧张状态,并且不再能够执行 Control.Invoke 代码。因此工作线程挂起 Invoke 调用并且不会释放锁。由于工作线程无法完成,UI 线程永远挂在锁上,形成死锁。

Control.Invoke 源自 .NET 1.0,该框架的一个版本在与线程相关的代码中存在多个严重的设计错误。虽然它们的目的是提供帮助,但它们只是为程序员设置了容易陷入的死亡陷阱。 Control.Invoke 的独特之处在于它是never正确使用它。

区分 Control.Invoke 和 Control.BeginInvoke。你只曾need当需要它的返回值时调用。请注意您是如何不这么做的,使用 BeginInvoke 就足够了,并且可以立即解决死锁。您可以考虑使用 Invoke 从 UI 获取值,以便可以在工作线程中使用它。但这会引发其他主要线程问题,即线程竞争错误,工作人员不知道 UI 处于什么状态。比如说,用户可能正忙于与其交互,输入新值。你无法知道你获得了什么值,它很容易是陈旧的旧值。不可避免地会导致 UI 和正在完成的工作之间不匹配。避免这种不幸的唯一方法是prevent用户无需输入新值,只需使用 Enable = false 即可轻松完成。但现在使用 Invoke 已经没有意义了,不妨在启动线程时传递该值。

所以使用BeginInvoke已经足以解决问题了。但这不是你应该停止的地方。 Click 事件处理程序中的这些锁没有任何意义,它们所做的只是使 UI 无响应,让用户感到非常困惑。您必须做的是将这些按钮的启用属性设置为false。将它们设置回true当工人完成时。现在它不会再出错了,您不需要锁,并且用户可以获得良好的反馈。

还有一个您尚未遇到但必须解决的严重问题。 UserControl 无法控制其生命周期,当用户关闭它所在的表单时,它就会被释放。但这与工作线程执行完全不同步,即使控件已经死了,它仍然会不断调用 BeginInvoke。这将使你的程序成为炸弹,希望是在 ObjectDisposeException 上。锁无法解决的线程竞争错误。表单必须提供帮助,它必须主动阻止用户关闭它。关于此错误的一些注释this Q+A https://stackoverflow.com/a/1732361/17034.

为了完整起见,我应该提到像这样的代码可能会遇到的第三个最常见的线程错误。它没有正式名称,我称之为“firehose bug”。当工作线程过于频繁地调用 BeginInvoke,从而给 UI 线程提供太多工作要做时,就会发生这种情况。发生很容易,每秒调用它超过一千次就足够了。 UI 线程开始消耗 100% 的核心,试图跟上调用请求,但永远无法跟上。很容易看出,它停止绘制自身并响应输入,以及以较低优先级执行的任务。这需要以合乎逻辑的方式解决,每秒更新 UI 超过 25 次只会产生人眼无法观察到的模糊,因此毫无意义。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从 UI 调用 System.Threading.Thread 时锁挂起 的相关文章

  • “benaphores”值得在现代操作系统上实施吗?

    当我还是一名 BeOS 程序员时 我读过本文 http www haiku os org legacy docs benewsletter Issue1 26 html Engineering1 26作者 Benoit Schillings
  • 如何在C++中实现模板类协变?

    是否可以以这样一种方式实现类模板 如果模板参数相关 一个对象可以转换为另一个对象 这是一个展示这个想法的例子 当然它不会编译 struct Base struct Derived Base template
  • fgets() 和 Ctrl+D,三次才能结束?

    I don t understand why I need press Ctrl D for three times to send the EOF In addition if I press Enter then it only too
  • 如何在我的应用程序中使用 Windows Key

    Like Windows Key E Opens a new Explorer Window And Windows Key R Displays the Run command 如何在应用程序的 KeyDown 事件中使用 Windows
  • 如何在 WPF RichTextBox 中跟踪 TextPointer?

    我正在尝试了解 WPF RichTextBox 中的 TextPointer 类 我希望能够跟踪它们 以便我可以将信息与文本中的区域相关联 我目前正在使用一个非常简单的示例来尝试弄清楚发生了什么 在 PreviewKeyDown 事件中 我
  • 写入和读取文本文件 - C# Windows 通用平台应用程序 Windows 10

    有用 但在显示任何内容之前 您必须在文本框中输入内容 我想那是因为我使用了 TextChanged 事件处理程序 如果我希望它在没有用户交互的情况下显示文本文件的内容 我应该使用哪个事件处理程序 因此 我想在按下按钮时将一些数据写入 C W
  • 为什么模板不能位于外部“C”块内?

    这是一个后续问题一个答案 https stackoverflow com questions 4866433 is it possible to typedef a pointer to extern c function type wit
  • Windows 窗体不会在调试模式下显示

    我最近升级到 VS 2012 我有一组在 VS 2010 中编码的 UI 测试 我试图在 VS 2012 中启动它们 我有一个 Windows 窗体 在开始时显示使用 AssemblyInitialize 属性运行测试 我使用此表单允许用户
  • 编译的表达式树会泄漏吗?

    根据我的理解 JIT 代码在程序运行时永远不会从内存中释放 这是否意味着重复调用 Compile 表达式树上会泄漏内存吗 这意味着仅在静态构造函数中编译表达式树或以其他方式缓存它们 这可能不那么简单 正确的 他们可能是GCed Lambda
  • Windows 10 中 Qt 桌面应用程序的缩放不当

    我正在为 Windows 10 编写一个简单的 Qt Widgets Gui 应用程序 我使用的是 Qt 5 6 0 beta 版本 我遇到的问题是它根本无法缩放到我的 Surfacebook 的屏幕上 这有点难以判断 因为 SO 缩放了图
  • 网络参考共享类

    我用 Java 编写了一些 SOAP Web 服务 在 JBoss 5 1 上运行 其中两个共享一个类 AddressTO Web 服务在我的 ApplycationServer 上正确部署 一切都很顺利 直到我尝试在我的 C 客户端中使用
  • AccessViolationException 未处理

    我正在尝试使用史蒂夫 桑德森的博客文章 http blog stevensanderson com 2010 01 28 editing a variable length list aspnet mvc 2 style 为了在我的 ASP
  • 将应用程序从 Microsoft Access 迁移到 VB 或 C#.NET

    我目前正试图说服管理层需要将我们的应用程序之一移植到 NET 该应用程序已经发展成为 Access 中的一个庞然大物 SQL 后端 拥有 700 个链接表 650 个表单 子表单 130 个模块和 850 个查询 我几乎知道这样做的所有主要
  • 在 URL 中发送之前对特殊字符进行百分比编码

    我需要传递特殊字符 如 等 Facebook Twitter 和此类社交网站的 URL 为此 我将这些字符替换为 URL 转义码 return valToEncode Replace 21 Replace 23 Replace 24 Rep
  • 已过时 - OpenCV 的错误模式

    我正在使用 OpenCV 1 进行一些图像处理 并且对 cvSetErrMode 函数 它是 CxCore 的一部分 感到困惑 OpenCV 具有三种错误模式 叶 调用错误处理程序后 程序终止 Parent 程序没有终止 但错误处理程序被调
  • 如何构建印度尼西亚电话号码正则表达式

    这些是一些印度尼西亚的电话号码 08xxxxxxxxx 至少包含 11 个字符长度 08xxxxxxxxxxx 始终以 08 开头 我发现这个很有用 Regex regex new Regex 08 0 9 0 9 0 9 0 9 0 9
  • 方法参数内的变量赋值

    我刚刚发现 通过发现错误 你可以这样做 string s 3 int i int TryParse s hello out i returns false 使用赋值的返回值是否合法 Obviously i is but is this th
  • Bing 地图运行时错误 Windows 8.1

    当我运行带有 Bing Map 集成的 Windows 8 1 应用程序时 出现以下错误 Windows UI Xaml Markup XamlParseException 类型的异常 发生在 DistanceApp exe 中 但未在用户
  • 如何使用 ReactiveList 以便在添加新项目时更新 UI

    我正在创建一个带有列表的 Xamarin Forms 应用程序 itemSource 是一个reactiveList 但是 向列表添加新项目不会更新 UI 这样做的正确方法是什么 列表定义 listView new ListView var
  • 将变量分配给另一个变量,并将一个变量的更改反映到另一个变量中

    是否可以将一个变量分配给另一个变量 并且当您更改第二个变量时 更改会瀑布式下降到第一个变量 像这样 int a 0 int b a b 1 现在 b 和 a 都 1 我问这个问题的原因是因为我有 4 个要跟踪的对象 并且我使用名为 curr

随机推荐