gpt4 book ai didi

c# - 从用户界面调用System.Threading.Thread时,锁将挂起。

转载 作者:行者123 更新时间:2023-11-30 15:17:36 26 4
gpt4 key购买 nike

编辑:
请查看问题历史记录,以了解未更改的问题,以免使评论无效。

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

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);
}
});
}

enter image description here

enter image description here

最佳答案

   Invoke((MethodInvoker)delegate ...

每当您在代码中使用 lock语句时,总是有引发死锁的风险。经典的线程错误之一。通常,您至少需要两个锁才能到达那里,并以错误的顺序获取它们。是的,您的程序中有两个。一个你宣布自己。还有一个您看不到的东西,因为它被埋在使Control.Invoke()工作的管道中。无法看到锁是导致死锁难以调试的问题。

您可以推断出来,Control.Invoke内部的锁是必需的,以确保在UI线程执行委托(delegate)目标之前,工作线程被阻塞。可能还有助于推理出程序死锁的原因。您启动了工作线程,它获得了 lockUcLoader锁并开始执行其工作,同时调用了Control.Invoke。现在,您在工作程序完成之前单击该按钮,它必然会阻塞。但是,这会使UI线程发生故障,并且不再能够执行Control.Invoke代码。因此,辅助线程将卡在Invoke调用上,并且不会释放锁。由于工作人员无法完成死锁城市,UI线程永远卡在锁上。

Control.Invoke始于.NET 1.0,该版本的框架在与线程相关的代码中存在若干严重的设计错误。虽然本来是有帮助的,但它们只是为程序员设置了陷阱,使他们大失所望。 Control.Invoke的独特之处在于使用它永远是不正确的。

区分Control.Invoke和Control.BeginInvoke。仅在需要返回值时才需要调用。请注意,使用BeginInvoke可以满足您的需要,它可以立即解决死锁。您可以考虑使用Invoke从UI中获取值,以便可以在辅助线程中使用它。但这会引起其他主要的线程问题,即线程竞赛错误,工作人员不知道UI处于什么状态。也就是说,用户可能正在忙于与之交互,并输入新值。您不知道获得什么值(value),这很容易成为陈旧的值(value)。不可避免地会在UI和正在完成的工作之间产生不匹配。避免这种麻烦的唯一方法是防止用户键入新值,这可以通过Enable = false轻松完成。但是现在使用Invoke不再有意义,您最好在启动线程时传递该值。

因此,使用BeginInvoke已经足以解决问题。但这不是您应该停止的地方。 Click事件处理程序中的这些锁没有意义,它们所做的只是使UI无响应,从而极大地使用户感到困惑。相反,您必须将这些按钮的Enable属性设置为false。工作人员完成后,将它们设置为true。现在它再也不会出错,您不需要锁,用户将获得良好的反馈。

您还没有遇到另一个严重的问题,但是必须解决。 UserControl对其生命周期没有控制权,当用户关闭托管它的表单时,它将被丢弃。但这与工作线程执行完全不同步,即使控件已死为门钉,它仍会继续调用BeginInvoke。这将使您的程序炸弹,希望发生在ObjectDisposedException上。锁无法解决的线程竞赛错误。该表单有帮助,它必须积极阻止用户关闭它。有关 this Q+A中此错误的一些说明。

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

关于c# - 从用户界面调用System.Threading.Thread时,锁将挂起。,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45934404/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com