C#多線程函數如何傳參數和返回值
提起多線程,不得不提起 委托(delegates)這個概念.
我理解的委托就是 具有 同樣參數和返回值 的函數的集合.
比如
public delegate void MyDelegate(int arg); 就是這種形式的函數 void Myfuntion(int i); 的集合. 如何將一個函數加入 委托 的集合? MyDelegate dele = new MyDelegate(Myfuntion1); 再增加一個 dele += new MyDelegate(Myfuntion2);
...
委托函數 dele 就是 具有整數參數和空返回值的函數 Myfuntion1,2的集合.
調用這個委托函數
dele(1);
就是逐個調用 Myfuntion1,2,...
一般線程函數的聲明和啟動
Thread t = new Thread(new ThreadStart(MyFunction)); t.Start();
正是調用了沒有參數和返回值的 委托函數 ThreadStart
其中的參數MyFunction 是 這個委托函數中的一員.
很明顯 這樣無法傳參數和返回值,那我們該怎么辦?
答案就在委托 的BeginInvoke() 方法上, BeginInvoke() 也是(異步)啟動一個新線程.
例如
MyDelegate dele = new MyDelegate (MyFunction); dele.BeginInvoke(10,"abcd"); void MyFunction(int count, string str);
可以實現參數的傳遞.
如何收集線程函數 的 返回值?
與 BeginInvoke 對應 有個 EndInvoke 方法,而且運行完畢返回 IAsyncResult 類型的返回值.
這樣我們可以這樣收集 線程函數的 返回值
MyDelegate dele = new MyDelegate (MyFunction); IAsyncResult ref = dele.BeginInvoke(10,"abcd"); ... int result = dele.EndInvoke(ref); <----收集 返回值 int MyFunction(int count, string str); <----帶參數和返回值的 線程函數
提示:"線程間操作無效:從不是創建控件“XX”的線程訪問它"
一般來說,直接在子線程中對窗體上的控件操作是會出現異常,這是由於子線程和運行窗體的線程是不同的空間,因此想要在子線程來操作窗體上的控件,是不可能簡單的通過控件對象名來操作,但不是說不能進行操作,微軟提供了Invoke的方法,其作用就是讓子線程告訴窗體線程來完成相應的控件操作。 現在用一個用線程控制的進程條來說明,大致的步驟如下:
1. 創建Invoke函數,大致如下: /// /// Delegate function to be invoked by main thread /// private void InvokeFun() { if( prgBar.Value < 100 ) prgBar.Value = prgBar.Value + 1; } 2. 子線程入口函數: /// /// Thread function interface /// private void ThreadFun() { //Create invoke method by specific function MethodInvoker mi = new MethodInvoker( this.InvokeFun ); for( int i = 0; i < 100; i++ ) { this.BeginInvoke( mi ); Thread.Sleep( 100 ); } } 3. 創建子線程: Thread thdProcess = new Thread( new ThreadStart( ThreadFun ) ); thdProcess.Start(); 出現這個問題主要是因為在線程方法中操作了界面上的控件..lstPrime.Items.Add() 可以這樣改下.. //定義一個委托 public delegate void MyInvoke(string str); //定義一個操作界面的方法 private void UpdateUI(string str) { //增加項 this.lstPrime.Items.Add(str); } //在線程的方法中,即你的Generate方法.. //里面只要是涉及到Items.Add操作的都改成如下形式即可.. //比如lstPrime.Items.Add(2);改成: MyInvoke mi=new MyInvoke(UpdateUI); this.BeginInvoke(mi,new object[]{ "2 "});
============================================================================
BeginInvoke與Invoke的含義[轉載]BeginInvoke 方法真的是新開一個線程進行異步調用嗎?
參考以下代碼:
public delegate void treeinvoke();
private void UpdateTreeView()
{
MessageBox.Show(System.Threading.Thread.CurrentThread.Name);
}
private void button1_Click(object sender, System.EventArgs e)
{
System.Threading.Thread.CurrentThread.Name = "UIThread";
treeView1.BeginInvoke(new treeinvoke(UpdateTreeView));
}
看看運行結果,彈出的對話框中顯示的是 UIThread,這說明 BeginInvoke 所調用的委托根本就是在 UI 線程中執行的。
既然是在 UI 線程中執行,又何來“異步執行”一說呢?
我們再看看下面的代碼:
public delegate void treeinvoke();
private void UpdateTreeView()
{
MessageBox.Show(Thread.CurrentThread.Name);
}
private void button1_Click(object sender, System.EventArgs e)
{
Thread.CurrentThread.Name = "UIThread";
Thread th = new Thread(new ThreadStart(StartThread));
th.Start();
}
private void StartThread()
{
Thread.CurrentThread.Name = "Work Thread";
treeView1.BeginInvoke(new treeinvoke(UpdateTreeView));
}
再看看運行結果,彈出的對話框中顯示的還是 UIThread,這說明什么?這說明 BeginInvoke 方法所調用的委托無論如何都是在 UI 線程中執行的。
那 BeginInvoke 究竟有什么用呢?
在多線程編程中,我們經常要在工作線程中去更新界面顯示,而在多線程中直接調用界面控件的方法是錯誤的做法,具體的原因可以在看完我的這篇之后看看這篇:在多線程中如何調用Winform,如果你是大牛的話就不要看我這篇了,直接看那篇吧,反正那篇文章我沒怎么看懂。
Invoke 和 BeginInvoke 就是為了解決這個問題而出現的,使你在多線程中安全的更新界面顯示。
正確的做法是將工作線程中涉及更新界面的代碼封裝為一個方法,通過 Invoke 或者 BeginInvoke 去調用,兩者的區別就是一個導致工作線程等待,而另外一個則不會。
而所謂的“一面響應操作,一面添加節點”永遠只能是相對的,使 UI 線程的負擔不至於太大而以,因為界面的正確更新始終要通過 UI 線程去做,我們要做的事情是在工作線程中包攬大部分的運算,而將對純粹的界面更新放到 UI 線程中去做,這樣也就達到了減輕 UI 線程負擔的目的了。
而在那段更新樹節點的代碼中,其實唯一起作用的代碼是:System.Threading.Thread.Sleep(100);,它使 UI 線程有了處理界面消息的機會,其實 數碼幽靈 將問題復雜化了,只要以下的代碼就可以很好的工作了。
private void button1_Click_(object sender, System.EventArgs e)
{
TreeNode tn;
for(int i=0;i<100000;i++)
{
tn=new TreeNode (i.ToString());
this.treeView1.Nodes[0].Nodes.Add(tn);
if (i%100 == 0) Application.DoEvents();
}
}
為了您的安全,請只打開來源可靠的網址
來自: http