繁体   English   中英

线程控制.Invoke

[英]Thread Control.Invoke

我有一个功能

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.

问题是什么?

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

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

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

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需要访问的任何变量来使用MethodInvoker

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

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

需要调用另一个(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; } } 

我总是发现这篇文章对这个特殊问题很有帮助。

在您的示例中,您尝试从未创建控件的线程修改各种控件。 要根据您的示例解决此问题,请执行此操作(假设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的观点,我已经注释掉了中止线程的调用。 线程将自行结束。 没有理由以这种方式中止它。

它是线程中控件工作的最佳方式。

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

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

请享用...

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

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

暂无
暂无

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

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