簡體   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