简体   繁体   English

线程控制.Invoke

[英]Thread Control.Invoke

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? 问题是什么?

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) 除了创建控件的“句柄”之外的任何线程(通常只有一个UI线程),不要触摸任何控件属性或方法(除了明确列出的那些没有)
  • Don't block the UI thread for any significant length of time, or you'll make the application unresponsive 不要在任何相当长的时间内阻止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 . 为了从不同的线程与UI交互,您需要使用委托“调用”对UI线程的调用并调用Control.Invoke / BeginInvoke You can test whether or not you need to call Invoke using the InvokeRequired property, but these days I personally tend to just do it anyway - there's not much penalty for invoking when you don't need to. 可以测试是否需要使用InvokeRequired属性调用Invoke ,但是现在我个人倾向于只是这样做 - 当你不需要时调用没有太多的惩罚。

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 MethodInvoker by capturing any variables you need to access within the delegate. 通常,您可以通过捕获MethodInvoker需要访问的任何变量来使用MethodInvoker

See my threading tutorial or Joe Albahari's for more details. 有关详细信息,请参阅我的线程教程Joe Albahari's

As a secondary matter, I see you're using Thread.Abort - in fact on your own thread, despite it having other calls after it. 作为次要问题,我看到你正在使用Thread.Abort - 事实上在你自己的线程上,尽管它之后还有其他调用。 Why? 为什么? Aborting any thread other than 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... 中止比你自己以外的任何线程是一个“只有紧急”呼叫类型(这通常应遵循由应用正在反正卸载),我看不出有任何理由中止当前线程时,有仍有许多工作之后进行。 ..

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; } } 

I've always found this article helpful 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. 为了强调@Jon Skeet的观点,我已经注释掉了中止线程的调用。 The thread will end of its own accord. 线程将自行结束。 There's no reason to abort it in this fashion. 没有理由以这种方式中止它。

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... 请享用...

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 它必须调用...但是调用必须等待主线程我的意思是你没有得到这种方式的错误但是如果你想同时进行多个进程只是创建一个以上的线程,那么这并非行得并行

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 = "";
}
}

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

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