我有一个生成BackgroundWorker的表单,它应该更新表单自己的文本框(在主线程上),因此Invoke((Action) (...));调用.
如果HandleClosingEvent我只是做bgWorker.CancelAsync()然后我得到ObjectDisposedException的Invoke(...)电话,可以理解.但是,如果我坐在那里HandleClosingEvent等待bgWorker完成,那么.Invoke(...)永远不会返回,也是可以理解的.
任何想法如何关闭此应用程序而不会出现异常或死锁?
以下是简单Form1类的3个相关方法:
public Form1() {
InitializeComponent();
Closing += HandleClosingEvent;
this.bgWorker.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) {
while (!this.bgWorker.CancellationPending) {
Invoke((Action) (() => { this.textBox1.Text = Environment.TickCount.ToString(); }));
}
}
private void HandleClosingEvent(object sender, CancelEventArgs e) {
this.bgWorker.CancelAsync();
/////// while (this.bgWorker.CancellationPending) {} // deadlock
}
Run Code Online (Sandbox Code Playgroud) 我仍然受到WinForm UI中背景线程的困扰.为什么?以下是一些问题:
我正在寻找这个问题的优雅解决方案,但在我了解我正在寻找的具体内容之前,我想我会澄清问题.这是为了解决一般性问题并在其背后提出一个更具体的例子.对于这个例子,假设我们正在通过互联网传输大量数据.用户界面必须能够显示正在进行的传输的进度对话框.进度对话框应该持续快速更新(每秒更新5到20次).用户可以随时关闭进度对话框,并在需要时再次调用它.而且,让我们假装参数,如果对话框可见,它必须处理每个进度事件.用户可以单击进度对话框上的取消,并通过修改事件参数,取消操作.
现在我需要一个适合以下限制条件的解决方案:
那么,鉴于上述限制,这可以解决吗?我搜索并挖掘了无数的博客和讨论,唉,我还是空手而归.
更新:我确实意识到这个问题没有简单的答案.我只在这个网站上呆了几天,我见过一些有很多回答问题经验的人.我希望这些人中的一个能够充分解决这个问题,这样我就不会花费一周时间来建立一个合理的解决方案.
更新#2:好的,我将尝试更详细地描述问题,看看有什么(如果有的话)震动.允许我们确定其状态的以下属性引起了一些问题...
Control.InvokeRequired =记录如果在当前线程上运行或者IsHandleCreated为所有父项返回false,则返回false.我很担心InvokeRequired实现有可能抛出ObjectDisposedException或甚至可能重新创建对象的句柄.并且由于InvokeRequired在我们无法调用(正在进行Dispose)时可以返回true,并且它可以返回false,即使我们可能需要使用invoke(正在创建),这在所有情况下都不可信任.唯一可以看到我们可以信任的地方InvokeRequired返回false是当IsHandleCreated在调用之前和之后都返回true时(BTW,InvokeRequired的MSDN文档确实提到了对IsHandleCreated的检查).
Control.IsHandleCreated =如果已为控件分配了句柄,则返回true;否则返回true.否则,错误.虽然IsHandleCreated是一个安全的调用,但如果控件正在重新创建它的句柄,它可能会崩溃.这个潜在的问题似乎可以通过在访问IsHandleCreated和InvokeRequired时执行锁定(控制)来解决.
Control.Disposing =如果控件处于处理过程中,则返回true.
......我的头疼了:(希望上面的信息可以为那些遇到这些麻烦的人提供更多的解决方案.我很感激你的思考周期.
关闭麻烦......以下是Control.DestroyHandle()方法的后半部分:
if (!this.RecreatingHandle && (this.threadCallbackList != null))
{
lock (this.threadCallbackList)
{
Exception exception = new ObjectDisposedException(base.GetType().Name);
while (this.threadCallbackList.Count > 0)
{
ThreadMethodEntry entry = (ThreadMethodEntry) this.threadCallbackList.Dequeue();
entry.exception = exception;
entry.Complete();
}
}
}
if ((0x40 & ((int) ((long) UnsafeNativeMethods.GetWindowLong(new …Run Code Online (Sandbox Code Playgroud) 我在C#中遇到多线程问题.我使用一个事件来更新另一个线程的表单中的标签,当然我需要使用Invoke()命令.那部分也很好.但是,用户可以关闭表单,如果在不幸的时间发送事件,程序可能会崩溃.
所以,我想我会简单地覆盖窗体的Dispose()方法,在锁定代码中将布尔值设置为true,并检查布尔值并在锁定代码中调用事件.
但是,每次关闭表单时程序都会完全冻结.
以下是代码中提到的部分:
private object dispose_lock = new object();
private bool _disposed = false;
private void update(object sender, EventArgs e)
{
if (InvokeRequired)
{
EventHandler handler = new EventHandler(update);
lock (dispose_lock)
{
if (_disposed) return;
Invoke(handler); // this is where it crashes without using the lock
}
return;
}
label.Text = "blah";
}
protected override void Dispose(bool disposing)
{
eventfullObject.OnUpdate -= update;
lock (dispose_lock) // this is where it seems to freeze
{
_disposed = true; // this is …Run Code Online (Sandbox Code Playgroud) 我刚刚开始学习c#中的线程和方法,但我遇到了一个我无法找到解决方案的问题.
我制作了一个基本的C#表单程序,通过启动一个线程并调用委托来不断更新和显示一个数字.
在Form1_load上启动新线程:
private void Form1_Load(object sender, EventArgs e)
{
t = new System.Threading.Thread(DoThisAllTheTime);
t.Start();
}
Run Code Online (Sandbox Code Playgroud)
Public void DoThisAllTheTime(不断更新数字):
public void DoThisAllTheTime()
{
while(true)
{
if (!this.IsDisposed)
{
number += 1;
MethodInvoker yolo = delegate() { label1.Text = number.ToString(); };
this.Invoke(yolo);
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在当我单击表单的X按钮时,我得到以下异常:
'System.Windows.Forms.dll中发生了'System.ObjectDisposedException'类型的未处理异常
无法更新已删除的对象'
虽然我确实检查了表格是否被处理.
编辑:我添加了catch(ObjectDisposedException ex)到修复问题的代码.工作代码:
public void DoThisAllTheTime()
{
while(true)
{
number += 1;
try {
MethodInvoker yolo = delegate() { label1.Text = number.ToString(); };
this.Invoke(yolo);
}
catch (ObjectDisposedException ex)
{
t.Abort();
}
}
}
Run Code Online (Sandbox Code Playgroud) 灵感来自我自己在多线程Winforms应用程序方面的经验,以及诸如此类的问题
我想出了一个非常简单的模式,我想验证它的健全性.
基本上我正在创建(并在整个应用程序的生命周期内运行)BGW,其唯一目的是同步调用请求.考虑:
public MainForm()
{
InitializeComponent();
InitInvocationSyncWorker();
}
private void InitInvocationSyncWorker()
{
InvocationSync_Worker.RunWorkerAsync();
}
private void InvocationSync_Worker_DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(Timeout.Infinite);
}
void InvokeViaSyncWorker(Action guiAction)
{
InvocationSync_Worker.ReportProgress(0, guiAction);
}
private void InvocationSync_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (IsDisposed) return; //we're in the GUI thread now, so no race condition right?
var action = (Action) e.UserState;
action();
}
public void SomeMethodCalledFromAnyThread() //Sample usage
{
InvokeViaSyncWorker(() => MyTextBox.Text = "Hello from another thread!"));
}
Run Code Online (Sandbox Code Playgroud)
当然,这不是最经济的方法(让线程保持活力),但如果它有效并且我没有错过任何东西,那肯定是我见过的最简单的.
反馈非常感谢!