【问题标题】:Thread accesing .NET control issue线程访问 .NET 控件问题
【发布时间】:2023-03-21 18:28:01
【问题描述】:

我有两个看起来像这样的线程

pbDB_running = true; // start progress bar

Thread connectDb = new Thread(new ThreadStart(ConnectToDb));
Thread runProgress = new Thread(new ThreadStart(RunpbDB));

connectDb.Start();
runProgress.Start();

connectDb.Join();  //wait untill the connection is done

pbDB_running = false; //stop the progress bar

您可能已经猜到了,ConnectToDb 用于连接数据库,而 runpbDB 用于在界面上运行进度条。进度条 (pbDB) 是在设计视图上通过拖放创建的 Windows.Forms 控件。 runProgress 线程正在运行 RunpbDB(),看起来像这样:

private void RunpbDB()
{
    while (pbDB_running)
    {
        if (pbDB.Value == 100) pbDB.Value = 0;
        else pbDB.Value += 1;
    }
    pbDB.Value = 0;
} 

当两个线程启动时,我在 RunpbDB() 中得到以下异常:

Cross-thread operation not valid: Control 'pbDB' accessed from a thread other than the thread it was created on.

我能做些什么来克服这种情况?

【问题讨论】:

    标签: c# .net multithreading controls


    【解决方案1】:

    您是否考虑过使用BackgroundWorker?这可能会让你的生活更轻松。您可以设置两个,一个用于数据库调用,另一个用于进度条。只需监听后台工作人员 ProgressChangedRunWorkerCompleted 事件。

    更多关于MSDN的信息

    【讨论】:

    • BackgroundWorker 不会在这里工作,因为 peter 想在 UI 线程上显示进度。
    • @ZafarYousafi BackgroundWorker 可以满足 Peter 想要实现的目标。是的,在当前的形式中,使用全局 pbDB_running 是行不通的,但要重新编写代码以适应后台工作人员,Peter 可以实现他想要的。
    • @KevinBrydon 我没有那样想。来自我的 +1。
    【解决方案2】:

    使用Control.Invoke 方法来解决这个问题。你的整个解决方案将变成

    private void RunpbDB()
    {
        while (pbDB_running)
        {
            Invoke((Action)(()=>{
            if (pbDB.Value == 100) pbDB.Value = 0;
            else pbDB.Value += 1;}));
        }
            Invoke((Action)(()=>{pbDB.Value = 0;});
    } 
    

    【讨论】:

    • 在这种情况下,runProgress 线程仅执行第一个 Invoke,然后仅在 connectDB 完成工作后执行 RunpbDB 函数的其余部分。
    【解决方案3】:

    您可以使用类似于 pbDB.InvokeRequired 的内容,如果是这样,请调用 pbDB.Invoke 以在 UI 线程上执行您的操作。

    如果您知道它将始终在与 UI 线程不同的线程上完成,则不需要检查。

    Here is a link to some code on this and other ways to accomplish this.

    您也可以使用BackgroundWorker

    【讨论】:

      【解决方案4】:

      这是微软为其 .NET 技术强加的安全措施。它基本上发生在您从单独的线程访问 winforms 元素时,即不在运行 GUI winforms 的主线程中。解决方案是为您的 RunpbDB 方法创建一个委托。请在此处查看解决方案Best Way to Invoke Any Cross-Threaded Code?。也在这里:How to update the GUI from another thread in C#?

      【讨论】:

        【解决方案5】:

        如果您无法访问 .NET 4.0,只需让您的生活更轻松,并为此使用 BackgroundWorker。如果您可以使用 4.0+,请使用 TPL。如果可以使用 4.5,则可以使用新的 async/await 功能。 Stack Overflow 上有很多例子。 Here 是 Stephen Cleary 比较它们的链接。

        【讨论】:

          【解决方案6】:

          VS 团队在 2.0 中不鼓励在涉及 UI 线程时进行跨线程操作调用(在此之前出于安全原因可能。有两种方法可以解决此问题。简单的方法是设置静态属性

          System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls
          

          设置为 false,然后从您的应用程序全局禁用此检查。但是任何人都没有建议这种解决方案,甚至我也没有建议,因为它再次打开了安全漏洞。 另一种方法是,在方法中先检查UI控件是否需要Invoke,如果需要则使用控件的invoke方法再次调用当前方法,然后返回。代码可以更好的表达我想说的意思

          private void RunpbDB()
              {
                  if (pbDB.InvokeRequired)
                  {
                      pbDB.Invoke(new Action(RunpbDB));
                      return;
                  }
                  while (pbDB_running)
                  {
                      if (pbDB.Value == 100) pbDB.Value = 0;
                      else pbDB.Value += 1;
                  }
                  pbDB.Value = 0;
              }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2011-07-28
            • 2012-09-20
            • 2011-02-27
            • 1970-01-01
            • 1970-01-01
            • 2018-12-29
            相关资源
            最近更新 更多