线程控制.Invoke

时间:2009-09-14 19:28:51

标签: c# multithreading

我有一个功能

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

在Form_Load函数中像这样;

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

但是当我运行它时;

ShowAllFly函数中的

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

有什么问题?

5 个答案:

答案 0 :(得分:49)

Windows窗体中有两个主要的线程规则:

  • 除了创建控件的“句柄”之外的任何线程(通常只有一个UI线程),不要触摸任何控件属性或方法(除了明确列出的那些)之外的其他线程。
  • 不要阻止UI线程任何相当长的时间,否则你会使应用程序无响应

为了从不同的线程与UI进行交互,您需要使用委托“调用”来调用UI线程并调用Control.Invoke / BeginInvoke。你可以测试你是否需要使用Invoke属性来调用InvokeRequired,但是这些天我个人倾向于只是这样做 - 在调用时没有太多的惩罚你不需要。

C#3中的Lambda表达式(或C#2中的匿名方法)使这一点变得更加愉快。

例如,您可以使用:

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

所有括号都会受到影响,所以如果您使用C#3,可能需要添加这样的扩展方法:

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

然后你可以这样做:

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

这是一个很简单的好事。通常,您可以通过捕获代理中需要访问的任何变量来使用MethodInvoker

有关详细信息,请参阅my threading tutorialJoe Albahari's

作为次要问题,我看到你正在使用Thread.Abort - 实际上是在你自己的线程上,尽管它之后还有其他调用。为什么?中止任何线程其他而不是你自己的是一个“紧急情况”类型的调用(通常应该在应用程序被卸载之后),我看不出任何理由中止当前线程以后还有工作要做......

答案 1 :(得分:6)

另一个(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;
    }
}

答案 2 :(得分:4)

我总是发现this article对这个特定问题很有帮助。

在您的示例中,您尝试从未创建控件的线程修改各种控件。要根据您的示例解决此问题,请执行此操作(假设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;
    } );
}

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

答案 3 :(得分:1)

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

首先,您必须在“单线程单元”线程中使用。

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

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

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

最后,您必须按照以下步骤进行控件更改:

...

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

享受...

答案 4 :(得分:0)

它必须调用...但是调用必须等待主线程我的意思是你没有这样得到错误但是如果你想同时进行多个进程只是创建一个以上的话,这并不是非常平行的工作螺纹

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 ();

同时处理两个进程

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