C# 线程控制.调用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1423446/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 16:23:07  来源:igfitidea点击:

Thread Control.Invoke

c#multithreading

提问by atromgame

I have a function

我有一个功能

public void ShowAllFly()
{  
        cbFly.Items.Clear();
        cbFly.Items.Add("U?u? Se?iniz...");

        dsFlyTableAdapters.tblFlyTableAdapter _t=new KTHY.dsFlyTableAdapters.tblFlyTableAdapter();
        dsFly _mds = new dsFly();
        _mds.EnforceConstraints = false;
        dsFly.tblFlyDataTable _m = _mds.tblFly;
        _t.Fill(_m);
        foreach (DataRow _row in _m.Rows)
        {
            cbFly.Items.Add(_row["FlyID"].ToString()+"-"+_row["FlyName"].ToString() + "-" + _row["FlyDirection"].ToString() + "-" + _row["FlyDateTime"].ToString());
        }
        _Thread.Abort();
        timer1.Enabled = false;
        WaitPanel.Visible = false;
}

In Form_Load Function Like this;

在 Form_Load 函数中像这样;

{
    _Thread = new System.Threading.Thread(new System.Threading.ThreadStart(ShowAllFly));
    _Thread.Start();
    _Thread.Priority = System.Threading.ThreadPriority.Normal;
}

But When I run it;

但是当我运行它时;

in ShowAllFly function

在 ShowAllFly 函数中

cbFly.Items.Clear(); ----  HERE Gives ERROR  LIKE  Control.Invoke must be used to interact with controls created on a separate thread.

What is the problem?

问题是什么?

采纳答案by Jon Skeet

There are two golden rules of threading in Windows Forms:

Windows 窗体中的线程有两条黄金法则:

  • Don't touch any control properties or methods (other than those explicitly listed as being okay) from any thread other than the one which created the control's "handle" (usually there's just one UI thread)
  • Don't block the UI thread for any significant length of time, or you'll make the application unresponsive
  • 除了创建控件的“句柄”的线程(通常只有一个 UI 线程)之外,不要从任何线程中触摸任何控件属性或方法(除了那些明确列为可以的)
  • 不要长时间阻塞 UI 线程,否则会使应用程序无响应

In order to interact with the UI from a different thread, you need to "marshall" the call to the UI thread, using a delegate and calling Control.Invoke/BeginInvoke. You cantest whether or not you need to call Invokeusing the InvokeRequiredproperty, but these days I personally tend to just do it anyway - there's not much penalty for invoking when you don't need to.

为了从不同的线程与 UI 交互,您需要“编组”对 UI 线程的调用,使用委托并调用Control.Invoke/ BeginInvoke。您可以测试是否需要Invoke使用该InvokeRequired属性进行调用,但现在我个人倾向于无论如何都这样做 - 在不需要时调用并没有太大的惩罚。

Lambda expressions in C# 3 (or anonymous methods in C# 2) make this a lot more pleasant as well.

C# 3 中的 Lambda 表达式(或 C# 2 中的匿名方法)也使这更令人愉快。

For instance, you could use:

例如,您可以使用:

cbFly.Invoke((MethodInvoker)(() => cbFly.Items.Clear()));

All the brackets get in the way a bit, so you might want to add an extension method like this, if you're using C# 3:

所有的括号都有些碍手碍脚,因此如果您使用的是 C# 3,您可能需要添加这样的扩展方法:

public static void Invoke(this Control control, MethodInvoker action)
{
    control.Invoke(action);
}

Then you could do:

那么你可以这样做:

cbFly.Invoke(() => cbFly.Items.Clear());

which is a good deal simpler. Usually you can get away with using a MethodInvokerby capturing any variables you need to access within the delegate.

这要简单得多。通常,您可以MethodInvoker通过捕获您需要在委托中访问的任何变量来避免使用 a 。

See my threading tutorialor Joe Albahari'sfor more details.

有关更多详细信息,请参阅我的线程教程Joe Albahari 的教程

As a secondary matter, I see you're using Thread.Abort- in fact on your own thread, despite it having other calls after it. Why? Aborting any thread otherthan your own is an "emergencies only" type call (which should usually be followed by the app being unloaded anyway) and I can't see any reason to abort the current thread when there's still work to be done afterwards...

作为次要问题,我看到您正在使用Thread.Abort- 实际上是在您自己的线程上,尽管它之后还有其他调用。为什么?中止任何线程其他比你自己是一个“只有紧急”呼叫类型(这通常应遵循由应用正在反正卸载),我看不出有任何理由要中止当前线程时仍有待以后完成的工作。 ..

回答by Ralf de Kleine

Interaction on controls in another (ui)thread need to be invoked like so:

需要像这样调用另一个(ui)线程中控件的交互:

public delegate void ProcessResultDelegate(string result);
void ProcessResult(string result)
{
    if (textBox1.InvokeRequired)
    {
        var d = new ProcessResultDelegate(ProcessResult);
        d.Invoke(result);
    }
    else
    {
        textBox1.Text = result;
    }
}
public delegate void ProcessResultDelegate(string result);
void ProcessResult(string result)
{
    if (textBox1.InvokeRequired)
    {
        var d = new ProcessResultDelegate(ProcessResult);
        d.Invoke(result);
    }
    else
    {
        textBox1.Text = result;
    }
}

回答by Matt Davis

I've always found this articlehelpful on this particular issue.

我一直发现这篇文章对这个特定问题很有帮助。

In your example, you're trying to modify various controls from a thread that did not create the control. To get around this issue given your example, do this instead (assuming that the ShowAllFly() method is a method on your form):

在您的示例中,您尝试从未创建控件的线程修改各种控件。为了在您的示例中解决此问题,请改为执行此操作(假设 ShowAllFly() 方法是您表单上的一个方法):

public void ShowAllFly()
{
    Invoke((MethodsInvoker) delegate {
        cbFly.Items.Clear();
        cbFly.Items.Add("U?u? Se?iniz...");
        dsFlyTableAdapters.tblFlyTableAdapter _t =
            new KTHY.dsFlyTableAdapters.tblFlyTableAdapter();
        dsFly _mds = new dsFly();
        _mds.EnforceConstraints = false;
        dsFly.tblFlyDataTable _m = _mds.tblFly;
        _t.Fill(_m);
        foreach (DataRow _row in _m.Rows)
        {
            cbFly.Items.Add(_row["FlyID"].ToString() + "-" +
                            _row["FlyName"].ToString() + "-" +
                            _row["FlyDirection"].ToString() + "-" +
                            _row["FlyDateTime"].ToString());
        }
        //_Thread.Abort(); // WHY ARE YOU TRYING TO DO THIS?
        timer1.Enabled = false;
        WaitPanel.Visible = false;
    } );
}

Just to emphasize the point @Jon Skeet made, I've commented out the call to abort the thread. The thread will end of its own accord. There's no reason to abort it in this fashion.

只是为了强调@Jon Skeet 提出的观点,我已经注释掉了中止线程的调用。线程将自行结束。没有理由以这种方式中止它。

回答by Toprak

It must be invoke ... But invoke have to wait still main thread i mean you not get error this way but this is not exacly working parallel if you want to go more than one process at same time just create more then one thread

它必须是 invoke ... 但是 invoke 必须等待主线程,我的意思是你不会以这种方式得到错误,但是如果你想同时运行多个进程,这不是完全并行的,只需创建多个线程

Thread thread = new Thread(new delegate_method(method));//you must create delegate before
thread.start ();
Thread thread2 = new Thread(new delegate_method(method2));//you must create delegate before
thread.start ();

handle two process same time

同时处理两个进程

    void method ()
{
//do something here -- working background Remember can not control any UI control from here
finish_thread()
}

void method2 ()
{
//do something here -- working background Remember can not control any UI control from here
finish_thread()
}

void finish_thread()
{
if(invoke.Required)
{
//Here you have to call delegate method here with UI
BeginInvoke(new delegate_method(finish_thread));
}
else
{
//Now you can control UI thread from here and also you finished background work
//Do something working with UI thread
textBox.Text = "";
}
}

回答by Mohammad Fathi MiMFa

It`s the best way for work by controls in a thread.

这是通过线程中的控件进行工作的最佳方式。

First you have to use from a Single Thread Apartment thread.

首先,您必须从单线程单元线程使用。

...
Thread th = new Thread(yourThreadStart);
            th.SetApartmentState(ApartmentState.STA);
th.Start();
...

Next copy this method between your code!

接下来在您的代码之间复制此方法!

public static void SetControlThreadSafe(Control control, Action<object[]> action, object[] args)
{
      if (control.InvokeRequired)
            try { control.Invoke(new Action<Control, Action<object[]>, object[]>(SetControlThreadSafe), control, action, args); } catch { }
      else action(args);
}

Finally your controls changes must be done like below:

最后,您的控件更改必须按如下方式完成:

...

    SetControlThreadSafe(textbox1, (arg) =>
          {
                textbox1.Text = "I`m Working in a Thread";
          }, null);
...

Enjoy...

享受...