幾個進程在大多數情況下要包含很多的子線程,那么他們之間免不了的要互相傳遞很多的參數,那么參數怎么傳遞的呢?
主線程向子線程傳遞參數的方法
第一種方法:Thraed類有一個帶參數的委托類型的重載形式,這個委托的定義如下:
public delegate void ParameterizedThreadStart(Object obj)
這個Thread類的構造方法的定義如下:
public Thread(ParameterizedThreadStart start);
下面的代碼使用了這個帶參數的委托向線程傳遞一個字符串參數:
public static void myStaticParamThreadMethod(Object obj)
{
Console.WriteLine(obj);
}
static void Main(string[] args)
{
Thread thread = new Thread(myStaticParamThreadMethod);
thread.Start("通過委托的參數傳值");
}
注意這種形式,委托就是Thread要執行的方法,這個委托有一個類的實例對象作為參數。然后在Thread的Start()方法中把這個對象傳進去。
如果使用了不帶參數的委托,當然也能很正常的啟動線程,別學傻了。
第二種方法:定義一個類來傳遞參數
class Program { static void Main(string[] args) { MyData myData = new MyData("abcd", 1234); Thread thread = new Thread(myData.ThreadMethod); thread.Start(); Console.ReadKey(); } }//class //定義一個類傳遞參數 public class MyData { private string d1; private int d2; public MyData(string d1, int d2) { this.d1 = d1; this.d2 = d2; } public void ThreadMethod() { Console.WriteLine(d1); Console.WriteLine(d2); } }//class
這種方法的特點是:子線程的執行入口是在另一個類中,這樣正好可以借助這個類的成員函數,給子線程傳參。
第三種方法:定義一個新的線程類,讓所有的子線程類都繼承自這個類
abstract class MyThread
{
Thread thread = null;
abstract public void run();
public void Start()
{
if (thread == null)
{
thread = new Thread(run);
thread.Start();
}
}
}
class Utility : MyThread
{
private string d1;
private int d2;
public override void run()
{
Console.WriteLine(d1);
Console.WriteLine(d2);
}
}//class
其實上面的兩種方法的原理是一樣的,這是一個面向數據,一個面向線程。
子線程向主線程傳遞參數
這里看到是傳遞參數,也就是說子線程要調用主線程的一個方法,然后把參數傳遞給主線程的那個方法。說一下方法是前面已經講過的Invoke和BeginInvoke,但是那時調用主線程的方法並沒有傳遞參數,今天看一下帶參數的調用主線程的指定方法。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Threading; namespace WindowsFormsApplication3 { public partial class Form1 : Form { private int count = 0; private delegate void DoWorkUIThreadDelegate(); public Form1() { InitializeComponent(); } private void btnStart_Click(object sender, EventArgs e) { Thread thread = new Thread(ThreadMethod); thread.IsBackground = true; thread.Start(); } private void ThreadMethod() { while (true) { //lblResult.Text = DateTime.Now.ToString(); //這句話不能直接調用,因為子線程不能直接調用UI線程中的控件 if (this.InvokeRequired) { this.BeginInvoke(new DoWorkUIThreadDelegate(DoWorkUIThread), null); } else { DoWorkUIThread(); } //子線程還是可以訪問UI線程的普通變量的,只是不能訪問控件 //因為普通變量是屬於整個類的,屬於整個進程的,各個線程時共享的 //對訪問共享的數據,加一個lock的鎖更加的好 count++; Thread.Sleep(1000); } } private void DoWorkUIThread() { txtTime.Text = DateTime.Now.ToString() + " " + count; } } }
上面的這種情況似乎用不着子線程給UI線程返回數據,反正子線程可以訪問主線程的成員變量。但是另一種情況來了,當子線程不再UI線程所在的類的時候,也就是說子線程在一個工具類中,UI類new出來一個工具類完成一定的工作,UI類可以初始化工具類,但是工具類完成了一定任務后怎么通知UI類呢?現在有這種假設:
UI類要Socket連接網絡,現在有一個SocketUtil工具可以完成這項任務,所以UI類就New出來一個SocketUtil,然后調用指定的函數,把IP和Port傳遞進去。在完成了一些任務以后,SocketUtil要反饋一些信息給UI類。讓UI類顯示反饋的信息。現在面臨兩個問題:
1. UI類怎么知道SocketUtil完成了這項任務,然后取顯示數據呢?
2. UI類即使知道了什么時候顯示信息,那么要顯示的內容,UI類怎么知道是什么呢?
第一個問題的解決方法就是事件,利用事件就能在SocketUtil完成一些任務之后,通知UI類接下來怎么做。
第二個問題就是利用Invoke讓子線程給主線程調用主線程的函數完成任務的時候,給一些參數。
這里就直接在同一個類中展示一下:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Threading; namespace WindowsFormsApplication3 { public partial class Form1 : Form { private int count = 0; private delegate void DoWorkUIThreadDelegate(string name, int id); public Form1() { InitializeComponent(); } private void btnStart_Click(object sender, EventArgs e) { Thread thread = new Thread(ThreadMethod); thread.IsBackground = true; thread.Start(); } private void ThreadMethod() { string strName = "stemon"; int ID = 1; while (true) { //lblResult.Text = DateTime.Now.ToString(); //這句話不能直接調用,因為子線程不能直接調用UI線程中的控件 if (this.InvokeRequired) { object[] myArray = new object[2]; //類型的裝箱是自動的 //類型的拆箱要強制轉換 myArray[0] = strName; myArray[1] = ID; this.BeginInvoke(new DoWorkUIThreadDelegate(DoWorkUIThread), myArray); } else { //DoWorkUIThread(strName, ID); } //子線程還是可以訪問UI線程的普通變量的,只是不能訪問控件 //因為普通變量是屬於整個類的,屬於整個進程的,各個線程時共享的 //對訪問共享的數據,加一個lock的鎖更加的好 count++; Thread.Sleep(1000); } } private void DoWorkUIThread(string name, int id) { txtTime.Text = name + " " + id + " " + DateTime.Now.ToString() + " " + count; } } }
這個方法是這樣操作的,BeginInvoke可以傳遞一個數組,這個數組是boject類型的,這樣就可以把所有的參數都裝箱成object類型的,弄成一個object類型的數組,然后到調用方在拆封就可以了。
