简体   繁体   English

C#线程安全控件更新

[英]C# thread safe control update

Question : When a form and its controls are accessed from a thread, which one is more suitable?: 问题 :从线程访问表单及其控件时,哪个更合适?:

  1. invoke form and update its controls in form-invoke 调用表单并通过表单调用更新其控件

  2. invoke controls distinctly and update controls distinctly 分别调用控件并分别更新控件

Code

if (mainForm.InvokeRequired)
{
   mainForm.Invoke(
      (Action)(() =>
                  {
                     mainForm.label1.Text = "update";
                     mainForm.textBox1.Text = "update";
                     mainForm.button1.Text = "update";
                  }));
}
else
{
   mainForm.label1.Text = "update";
   mainForm.textBox1.Text = "update";
   mainForm.button1.Text = "update";
}


//OR

if (mainForm.label1.InvokeRequired)
{
   mainForm.Invoke((Action)(() => { mainForm.label1.Text = "update"; }));
}
else
{
   mainForm.label1.Text = "update";
}

Each time you Invoke , there's a delay and cost as we have to send a message to the UI thread, essentially the message being "please run this code". 每次Invoke ,都会产生延迟和成本,因为我们必须向UI线程发送一条消息,实质上该消息是“请运行此代码”。 Then we have to wait for the UI thread to pick up this message (it may currently be busy doing some other work), run the code we've asked it to, and for it to indicate that it's done so. 然后,我们必须等待UI线程接收到此消息(当前可能正在忙于其他工作),运行我们要求它执行的代码,并指示它已经完成了。 Then, finally, we can resume working after the Invoke call. 然后,最后,我们可以在Invoke调用后恢复工作。

Now, in the grand scheme of things, this cost is quite a low one. 现在,按照宏伟的计划,这一成本是相当低的。 So if you're just using Invoke once or twice, it's not worth considering. 因此,如果您仅使用一次或两次Invoke ,就不值得考虑。 However, if you were planning on executing a big loop and Invoke ing inside the loop, you may want to pause and reconsider. 但是,如果您打算执行一个大循环并在循环内Invoke ,您可能需要暂停并重新考虑。 It's usually better to move the Invoke outside of the loop instead. 通常最好将Invoke移到循环之外。

However , the UI thread is a precious resource. 但是 ,UI线程是宝贵的资源。 We don't want to spend time on that thread doing non UI related tasks. 我们不想把时间花在该线程做 UI相关的任务。

So if you have a loop doing a mixture of CPU-intensive work and UI updates, neither is necessarily appropriate. 因此,如果您有一个循环执行CPU密集型工作和UI更新的混合操作,那么两者都不是必须的。 In such a case, see if you can re-structure your code so that you prepare batches of UI updates within the loop and then occasionally (or after the loop if possible) do an Invoke to just perform those UI updates. 在这种情况下,请查看是否可以重新组织代码,以便在循环内准备一批 UI更新,然后偶尔(或在循环之后,如果可能)执行一次Invoke来仅执行那些UI更新。

In your case, you appear to already have a batch of UI updates to perform. 就您而言,您似乎已经要执行一批UI更新。 So prefer the code that Invoke s once to do all three: 因此,更喜欢一次Invoke的代码来完成所有三个操作:

if (mainForm.InvokeRequired)
{
   mainForm.Invoke(
      (Action)(() =>
                  {
                     mainForm.label1.Text = "update";
                     mainForm.textBox1.Text = "update";
                     mainForm.button1.Text = "update";
                  }));
}
else
{
   mainForm.label1.Text = "update";
   mainForm.textBox1.Text = "update";
   mainForm.button1.Text = "update";
}

But note that it's usually better to structure the code so that you're not duplicating the actual UI updates that you have above. 但是请注意,通常最好对代码进行结构化,以免重复上面的实际UI更新。 Something like: 就像是:

private void DoUpdate()
{
   if(mainForm.InvokeRequired)
   {
      mainForm.Invoke(DoUpdate);
      return;
   }
   mainForm.label1.Text = "update";
   mainForm.textBox1.Text = "update";
   mainForm.button1.Text = "update";
}

I usally use the ISynchronizeInvoke since all forms/usercontrols inherit this interface, and then call a method to execute your action on the ui thread (if the invocation is required), 我通常使用ISynchronizeInvoke,因为所有表单/用户控件都继承此接口,然后调用一种方法来对ui线程执行操作(如果需要调用),

 public void SafeExecute(IView form, Action methodToExecute)
 {
      // args chek....
      ISynchronizeInvoke view = form as ISynchronizeInvoke;
      if(view != null)
      {
        if (!View.InvokeRequired)
            {
                methodToExecute();
            }
            else
            {
                view.Invoke(methodToExecute, null);
            }
      }
      else methodToExecute();
 }

and then in your codebehind you can call this method like this (I assume your view implement an interface, in this case I call it IView) 然后在您的代码隐藏中可以这样调用此方法(我假设您的视图实现了一个接口,在这种情况下,我将其称为IView)

 SafeExecute(this, () => {
   mainForm.label1.Text = "update";
   mainForm.textBox1.Text = "update";
   mainForm.button1.Text = "update";
 })

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM