相关疑难解决方法(0)

如何在Form的Closing事件中停止BackgroundWorker?

我有一个生成BackgroundWorker的表单,它应该更新表单自己的文本框(在主线程上),因此Invoke((Action) (...));调用.
如果HandleClosingEvent我只是做bgWorker.CancelAsync()然后我得到ObjectDisposedExceptionInvoke(...)电话,可以理解.但是,如果我坐在那里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)

c# multithreading backgroundworker winforms

66
推荐指数
1
解决办法
5万
查看次数

在跨线程WinForm事件处理中避免Invoke/BeginInvoke的困境?

我仍然受到WinForm UI中背景线程的困扰.为什么?以下是一些问题:

  1. 显然是最重要的问题,除非我在创建它的同一个线程上执行,否则我无法修改控件.
  2. 如您所知,Invoke,BeginInvoke等在创建Control之后才可用.
  3. 即使在RequiresInvoke返回true之后,BeginInvoke仍然可以抛出ObjectDisposed,即使它没有抛出,如果控件被销毁,它也可能永远不会执行代码.
  4. 即使在RequiresInvoke返回true之后,Invoke也可以无限期地挂起等待由调用Invoke同时处理的控件执行.

我正在寻找这个问题的优雅解决方案,但在我了解我正在寻找的具体内容之前,我想我会澄清问题.这是为了解决一般性问题并在其背后提出一个更具体的例子.对于这个例子,假设我们正在通过互联网传输大量数据.用户界面必须能够显示正在进行的传输的进度对话框.进度对话框应该持续快速更新(每秒更新5到20次).用户可以随时关闭进度对话框,并在需要时再次调用它.而且,让我们假装参数,如果对话框可见,它必须处理每个进度事件.用户可以单击进度对话框上的取消,并通过修改事件参数,取消操作.

现在我需要一个适合以下限制条件的解决方案:

  1. 允许工作线程调用Control/Form上的方法并阻塞/等待,直到执行完成.
  2. 允许对话框本身在初始化等时调用相同的方法(因此不使用invoke).
  3. 对处理方法或调用事件不施加任何实施负担,解决方案应仅更改事件订阅本身.
  4. 适当地处理阻塞调用到可能正在处理的对话框.不幸的是,这并不像检查IsDisposed那么容易.
  5. 必须能够与任何事件类型一起使用(假设类型为EventHandler的委托)
  6. 不得将异常转换为TargetInvocationException.
  7. 该解决方案必须与.Net 2.0及更高版本配合使用

那么,鉴于上述限制,这可以解决吗?我搜索并挖掘了无数的博客和讨论,唉,我还是空手而归.

更新:我确实意识到这个问题没有简单的答案.我只在这个网站上呆了几天,我见过一些有很多回答问题经验的人.我希望这些人中的一个能够充分解决这个问题,这样我就不会花费一周时间来建立一个合理的解决方案.

更新#2:好的,我将尝试更详细地描述问题,看看有什么(如果有的话)震动.允许我们确定其状态的以下属性引起了一些问题...

  1. Control.InvokeRequired =记录如果在当前线程上运行或者IsHandleCreated为所有父项返回false,则返回false.我很担心InvokeRequired实现有可能抛出ObjectDisposedException或甚至可能重新创建对象的句柄.并且由于InvokeRequired在我们无法调用(正在进行Dispose)时可以返回true,并且它可以返回false,即使我们可能需要使用invoke(正在创建),这在所有情况下都不可信任.唯一可以看到我们可以信任的地方InvokeRequired返回false是当IsHandleCreated在调用之前和之后都返回true时(BTW,InvokeRequired的MSDN文档确实提到了对IsHandleCreated的检查).

  2. Control.IsHandleCreated =如果已为控件分配了句柄,则返回true;否则返回true.否则,错误.虽然IsHandleCreated是一个安全的调用,但如果控件正在重新创建它的句柄,它可能会崩溃.这个潜在的问题似乎可以通过在访问IsHandleCreated和InvokeRequired时执行锁定(控制)来解决.

  3. Control.Disposing =如果控件处于处理过程中,则返回true.

  4. Control.IsDisposed =如果控件已被释放,则返回true.我正在考虑订阅Disposed事件并检查IsDisposed属性以确定BeginInvoke是否会完成.这里的一个大问题是在Disposing - > Disposed过渡期间缺少同步锁定.如果您订阅Disposed事件并在此之后验证Disposing == false && IsDisposed == false,您可能永远不会看到Disposed事件触发.这是因为Dispose的实现设置Disposing = false,然后设置Disposed = true.这为您提供了一个机会(无论多小),以便在已处理的控件上将Disposing和IsDisposed都读为false.

......我的头疼了:(希望上面的信息可以为那些遇到这些麻烦的人提供更多的解决方案.我很感激你的思考周期.

关闭麻烦......以下是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)

.net c# events multithreading winforms

48
推荐指数
3
解决办法
2万
查看次数

'死锁'只有一个被锁定的物体?

我在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)

.net c# multithreading locking winforms

6
推荐指数
2
解决办法
1839
查看次数

关闭Form(线程+调用)时出现异常

我刚刚开始学习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)

c# multithreading exception

5
推荐指数
1
解决办法
7450
查看次数

利用BackGroundWorker在Winforms控件上进行GUI操作的跨线程调用?

灵感来自我自己在多线程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)

当然,这不是最经济的方法(让线程保持活力),但如果它有效并且我没有错过任何东西,那肯定是我见过的最简单的.

反馈非常感谢!

.net c# synchronization backgroundworker winforms

1
推荐指数
1
解决办法
1045
查看次数