一、進程、線程及多線程的概念
什么是多線程呢?不理解。
那什么是線程呢?說到線程就不得不說說進程。我在網上搜索也搜索了一些資料,大部分所說的進程其實是很抽象的東西。通俗的來講,進程就是一個應用程序開始運行,那么這個應用程序就會存在一個屬於這個應用程序的進程。
那么線程就是進程中的基本執行單元,每個進程中都至少存在着一個線程,這個線程是根據進程創建而創建的,所以這個線程我們稱之為主線程。那么多線程就是包含有除了主線程之外的其他線程。如果一個線程可以執行一個任務,那么多線程就是可以同時執行多個任務。
以上的概念純屬個人理解,如有什么不對的地方,還請多多指正。
二、線程的基本知識
Thread 類
Thread 類是用於控制線程的基礎類,它存在於 System.Threading 命名空間。通過 Thread 可以控制當前應用程序域中線程的創建、掛起、停止、銷毀。
Thread 一些常用屬性:
Thread 一些常用方法:
Thread 的優先級:
三、多線程的簡單示例
下面就從簡單的多線程開始理解吧,這里我創建了一個控制台應用程序。
class Program { static void Main(string[] args) { ThreadDemoClass demoClass = new ThreadDemoClass(); //創建一個新的線程 Thread thread = new Thread(demoClass.Run); //設置為后台線程 thread.IsBackground = true; //開始線程 thread.Start(); Console.WriteLine("Main thread working..."); Console.WriteLine("Main thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString()); Console.ReadKey(); } } public class ThreadDemoClass { public void Run() { Console.WriteLine("Child thread working..."); Console.WriteLine("Child thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString()); } }
創建一個新的線程還可以使用 ThreadStart 委托的方式。如下:
//創建一個委托,並把要執行的方法作為參數傳遞給這個委托 ThreadStart threadStart = new ThreadStart(demoClass.Run); Thread thread = new Thread(threadStart);
執行結果:
根據以上的結果我們可以分析得到,主線程創建了一個子線程並啟動了它,但是主線程沒有等到子線程執行完成,而是繼續再往下執行的。
這就涉及到了線程異步或同步的問題了,這個我們后面再說。
繼續上面的問題,那么如果我想要等到子線程執行完成之后再繼續主線程的工作呢(當然,我覺得一般不會有這種需求)。
我們可以使用 Join() 這個方法,修改之后的代碼:
class Program { static void Main(string[] args) { ThreadDemoClass demoClass = new ThreadDemoClass(); //創建一個新的線程 Thread thread = new Thread(demoClass.Run); //設置為后台線程 thread.IsBackground = true; //開始線程 thread.Start(); //等待直到線程完成 thread.Join(); Console.WriteLine("Main thread working..."); Console.WriteLine("Main thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString()); Console.ReadKey(); } } public class ThreadDemoClass { public void Run() { Console.WriteLine("Child thread working..."); Console.WriteLine("Child thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString()); } }
執行結果:
上面的代碼相比之前就添加了一句 thread.Join(),它的作用就是用於阻塞后面的線程,直到當前線程完成之后。當然,還有其他的方法可以做到,比如我們現在把 thread.Join() 換成
下面這句代碼。
//掛起當前線程指定的時間 Thread.Sleep(100);
就當前的場景來說,這樣的確可以滿足需求,但是這樣做有一個弊端,就是,當子線程所執行的方法邏輯比較復雜耗時較長的時候,這樣的方式就不一定可以,雖然可以修改線程掛起的時間,但是這個執行的時間卻是不定的。所以,Thread.Sleep() 方法一般用來設置多線程之間執行的間隔時間的。
另外,Join() 方法也接受一個參數,該參數用於指定阻塞線程的時間,如果在指定的時間內該線程沒有終止,那么就返回 false,如果在指定的時間內已終止,那么就返回 true。
上面的這種使用多線程的方式只是簡單的輸出一段內容而已,多數情況下我們需要對線程調用的方法傳入參數和接收返回值的,但是上面這種方法是不接受參數並且沒有返回值的,那么我們可以使用 ParameterizedThreadStart 委托來創建多線程,這個委托可以接受一個 object 類型的參數,我們可以在這上面做文章。
class Program {static void Main(string[] args) { ThreadDemoClass demoClass = new ThreadDemoClass(); //創建一個委托,並把要執行的方法作為參數傳遞給這個委托 ParameterizedThreadStart threadStart = new ParameterizedThreadStart(demoClass.Run); //創建一個新的線程 Thread thread = new Thread(threadStart); //開始線程,並傳入參數 thread.Start("Brambling"); Console.WriteLine("Main thread working..."); Console.WriteLine("Main thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString()); Console.ReadKey(); } } public class ThreadDemoClass { public void Run(object obj) { string name = obj as string; Console.WriteLine("Child thread working..."); Console.WriteLine("My name is " + name); Console.WriteLine("Child thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString()); } }
執行結果:
PS:這里我沒有加這句代碼了(thread.IsBackground = true,即把當前線程設置為后台線程),因為使用 thread.Start() 啟動的線程默認為前台線程。那么前台線程和后台線程有什么區別呢?
前台線程就是系統會等待所有的前台線程運行結束后,應用程序域才會自動卸載。而設置為后台線程之后,應用程序域會在主線程執行完成時被卸載,而不會等待異步線程的執行完成。
那么上面的結果可以看到在多線程實現了參數的傳遞,可是它也只有一個參數呢。但是它接受的參數是 object 類型的(萬類之源),也就是說既可以是值類型或引用類型,也可以是自定義類型。(當然,自定義類型其實也是屬於引用類型的)下面我們使用自定義類型作為參數傳遞。
class Program { static void Main(string[] args) { ThreadDemoClass demoClass = new ThreadDemoClass(); //創建一個委托,並把要執行的方法作為參數傳遞給這個委托 ParameterizedThreadStart threadStart = new ParameterizedThreadStart(demoClass.Run); //創建一個新的線程 Thread thread = new Thread(threadStart); UserInfo userInfo = new UserInfo(); userInfo.Name = "Brambling"; userInfo.Age = 333; //開始線程,並傳入參數 thread.Start(userInfo); Console.WriteLine("Main thread working..."); Console.WriteLine("Main thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString()); Console.ReadKey(); } } public class ThreadDemoClass { public void Run(object obj) { UserInfo userInfo = (UserInfo)obj; Console.WriteLine("Child thread working..."); Console.WriteLine("My name is " + userInfo.Name); Console.WriteLine("I'm " + userInfo.Age + " years old this year"); Console.WriteLine("Child thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString()); } } public class UserInfo { public string Name { get; set; } public int Age { get; set; } }
執行結果:
使用自定義類型作為參數傳遞,理論上更多個參數也都是可以實現的。
四、線程池
使用 ThreadStart 和 ParameterizedThreadStart 創建線程還是比較簡單的,但是由於線程的創建和銷毀需要耗費一定的開銷,過多的使用線程反而會造成內存資源的浪費,從而影響性能,出於對性能的考慮,於是引入了線程池的概念。線程池並不是在 CLR 初始化的時候立刻創建線程的,而是在應用程序要創建線程來執行任務的時候,線程池才會初始化一個線程,初始化的線程和其他線程一樣,但是在線程完成任務之后不會自行銷毀,而是以掛起的狀態回到線程池。當應用程序再次向現成池發出請求的時候,線程池里掛起的線程會再度激活執行任務。這樣做可以減少線程創建和銷毀所帶來的開銷。線程池建立的線程默認為后台線程。
class Program { static void Main(string[] args) { ThreadDemoClass demoClass = new ThreadDemoClass(); //設置當沒有請求時線程池維護的空閑線程數 //第一個參數為輔助線程數 //第二個參數為異步 I/O 線程數 ThreadPool.SetMinThreads(5, 5); //設置同時處於活動狀態的線程池的線程數,所有大於次數目的請求將保持排隊狀態,直到線程池變為可用 //第一個參數為輔助線程數 //第二個參數為異步 I/O 線程數 ThreadPool.SetMaxThreads(100, 100); //使用委托綁定線程池要執行的方法(無參數) WaitCallback waitCallback1 = new WaitCallback(demoClass.Run1); //將方法排入隊列,在線程池變為可用時執行 ThreadPool.QueueUserWorkItem(waitCallback1); //使用委托綁定線程池要執行的方法(有參數) WaitCallback waitCallback2 = new WaitCallback(demoClass.Run1); //將方法排入隊列,在線程池變為可用時執行 ThreadPool.QueueUserWorkItem(waitCallback2,"Brambling"); UserInfo userInfo = new UserInfo(); userInfo.Name = "Brambling"; userInfo.Age = 33; //使用委托綁定線程池要執行的方法(有參數,自定義類型的參數) WaitCallback waitCallback3 = new WaitCallback(demoClass.Run2); //將方法排入隊列,在線程池變為可用時執行 ThreadPool.QueueUserWorkItem(waitCallback3, userInfo); Console.WriteLine(); Console.WriteLine("Main thread working..."); Console.WriteLine("Main thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString()); Console.ReadKey(); } } public class ThreadDemoClass { public void Run1(object obj) { string name = obj as string; Console.WriteLine(); Console.WriteLine("Child thread working..."); Console.WriteLine("My name is " + name); Console.WriteLine("Child thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString()); } public void Run2(object obj) { UserInfo userInfo=(UserInfo)obj; Console.WriteLine(); Console.WriteLine("Child thread working..."); Console.WriteLine("My name is " + userInfo.Name); Console.WriteLine("I'm " + userInfo.Age + " years old this year"); Console.WriteLine("Child thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString()); } } public class UserInfo { public string Name { get; set; } public int Age { get; set; } }
執行結果:
使用線程池建立的線程也可以選擇傳遞參數或不傳遞參數,並且參數也可以是值類型或引用類型(包括自定義類型)。看上面的結果發現了什么?沒錯,第一次執行的方法的線程ID為6,最后一次執行的方法的線程ID也為6。這就說明第一次請求線程池的時候,線程池建立了一個線程,當它執行完成之后就以掛起狀態回到了線程池,在最后一次請求的時候,再次喚醒了該線程執行任務。這樣就很容易理解了。
在這里我還發現了一個問題,就是,每次運行的時候,輸出的內容的順序都不一定是一樣的。(不只是線程池,前面的也是)因為,我沒有給任何線程設置優先級(線程池不能設置線程的優先級),這里其實就涉及到線程安全的問題了,很明顯現在這樣是非線程安全的。讓我舉個栗子形容一下的話,就像以前在學校下課了去吃飯一樣,一擁而上,毫無秩序。
線程安全就先不說,留在后面再說(包括前面所提到的線程同步的問題),這篇博客旨在理解多線程。因為我也沒有太深的理解。。。
上面我們已經實現了無參數和有參數以及自定義參數多線程的實例,可是還有一個共同的問題那就是都沒有返回值。當我們用多線程做實際開發的時候大部分都是會需要返回值的,那么我們可以使用成員變量來試試。
class Program { List<UserInfo> userInfoList = new List<UserInfo>(); static void Main(string[] args) { Program program = new Program(); ParameterizedThreadStart threadStart = new ParameterizedThreadStart(program.Run); Thread thread = null; UserInfo userInfo = null; for (int i = 0; i < 3; i++) { userInfo = new UserInfo(); userInfo.Name = "Brambling" + i.ToString(); userInfo.Age = 33 + i; thread = new Thread(threadStart); thread.Start(userInfo); thread.Join(); } foreach (UserInfo user in program.userInfoList) { Console.WriteLine("My name is " + user.Name); Console.WriteLine("I'm " + user.Age + " years old this year"); Console.WriteLine("Thread ID is:" + user.ThreadId); } Console.ReadKey(); } public void Run(object obj) { UserInfo userInfo = (UserInfo)obj; userInfo.ThreadId = Thread.CurrentThread.ManagedThreadId; userInfoList.Add(userInfo); } }
執行結果:
用上面這種方法勉強可以滿足返回值的需求,但是卻有很大的局限性,因為這里我使用的是成員變量,所以也就限制了線程調用的方法必須是在同一個類里面。
所以也就有了下面的方法,使用委托異步調用的方法。
五、異步委托
委托的異步調用有兩個比較重要的方法:BeginInvoke() 和 EndInvoke()
class Program { //定義一個委托類 private delegate UserInfo MyDelegate(UserInfo userInfo); static void Main(string[] args) { ThreadDemoClass demoClass = new ThreadDemoClass(); List<UserInfo> userInfoList = new List<UserInfo>(); UserInfo userInfo = null; UserInfo userInfoRes = null; //創建一個委托並綁定方法 MyDelegate myDelegate = new MyDelegate(demoClass.Run); for (int i = 0; i < 3; i++) { userInfo = new UserInfo(); userInfo.Name = "Brambling" + i.ToString(); userInfo.Age = 33 + i; //傳入參數並執行異步委托 IAsyncResult result = myDelegate.BeginInvoke(userInfo,null,null); //異步操作是否完成 while (!result.IsCompleted) { Thread.Sleep(100); Console.WriteLine("Main thread working..."); Console.WriteLine("Main thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString()); Console.WriteLine(); } //結束異步委托,並獲取返回值 userInfoRes = myDelegate.EndInvoke(result); userInfoList.Add(userInfoRes); } foreach (UserInfo user in userInfoList) { Console.WriteLine("My name is " + user.Name); Console.WriteLine("I'm " + user.Age + " years old this year"); Console.WriteLine("Thread ID is:" + user.ThreadId); } Console.ReadKey(); } } public class ThreadDemoClass { public UserInfo Run(UserInfo userInfo) { userInfo.ThreadId = Thread.CurrentThread.ManagedThreadId; Console.WriteLine("Child thread working..."); Console.WriteLine("Child thread ID is:" + userInfo.ThreadId); Console.WriteLine(); return userInfo; } }
執行結果:
BeginInvoke() 方法用於異步委托的執行開始,EndInvoke() 方法用於結束異步委托,並獲取異步委托執行完成后的返回值。IAsyncResult.IsCompleted 用於監視異步委托的執行狀態(true / false),這里的時間是不定的,也就是說一定要等到異步委托執行完成之后,這個屬性才會返回 true。如果異步委托的方法耗時較長,那么主線程會一直工作下去。
BeginInvoke() 是可以接受多個參數的,它的參數個數和參數類型取決於定義委托時的參數個數和類型,無論它有多少個參數,最后兩個參數都是不變的,下面我們會說到。
那么我們還可以用下面的方法 WaitOne(),自定義一個等待的時間,如果在這個等待時間內異步委托沒有執行完成,那么就會執行 while 里面的主線程的邏輯,反之就不會執行。
class Program { //定義一個委托類 private delegate UserInfo MyDelegate(UserInfo userInfo); static void Main(string[] args) { ThreadDemoClass demoClass = new ThreadDemoClass(); List<UserInfo> userInfoList = new List<UserInfo>(); UserInfo userInfo = null; UserInfo userInfoRes = null; //創建一個委托並綁定方法 MyDelegate myDelegate = new MyDelegate(demoClass.Run); for (int i = 0; i < 3; i++) { userInfo = new UserInfo(); userInfo.Name = "Brambling" + i.ToString(); userInfo.Age = 33 + i; //傳入參數並執行異步委托 IAsyncResult result = myDelegate.BeginInvoke(userInfo,null,null); //阻止當前線程,直到 WaitHandle 收到信號,參數為指定等待的毫秒數 while (!result.AsyncWaitHandle.WaitOne(1000)) { Console.WriteLine("Main thread working..."); Console.WriteLine("Main thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString()); Console.WriteLine(); } //結束異步委托,並獲取返回值 userInfoRes = myDelegate.EndInvoke(result); userInfoList.Add(userInfoRes); } foreach (UserInfo user in userInfoList) { Console.WriteLine("My name is " + user.Name); Console.WriteLine("I'm " + user.Age + " years old this year"); Console.WriteLine("Thread ID is:" + user.ThreadId); } Console.ReadKey(); } } public class ThreadDemoClass { public UserInfo Run(UserInfo userInfo) { userInfo.ThreadId = Thread.CurrentThread.ManagedThreadId; Console.WriteLine("Child thread working..."); Console.WriteLine("Child thread ID is:" + userInfo.ThreadId); Console.WriteLine(); return userInfo; } }
執行結果:
WaitOne() 方法只能用於監視當前線程的對象,如果要監視多個對象可以使用 WaitAny(WaitHandle[], int)或 WaitAll (WaitHandle[] , int) 這兩個方法。
class Program { //定義一個委托類 private delegate UserInfo MyDelegate(UserInfo userInfo); static void Main(string[] args) { ThreadDemoClass demoClass = new ThreadDemoClass(); List<UserInfo> userInfoList = new List<UserInfo>(); UserInfo userInfo = null; UserInfo userInfoRes = null; //創建一個委托並綁定方法 MyDelegate myDelegate = new MyDelegate(demoClass.Run); for (int i = 0; i < 3; i++) { userInfo = new UserInfo(); userInfo.Name = "Brambling" + i.ToString(); userInfo.Age = 33 + i; //傳入參數並執行異步委托 IAsyncResult result = myDelegate.BeginInvoke(userInfo,null,null); IAsyncResult result1 = myDelegate.BeginInvoke(userInfo, null, null); //定義要監視的對象,不能包含對同一對象的多個引用 WaitHandle[] waitHandles = new WaitHandle[] { result.AsyncWaitHandle, result1.AsyncWaitHandle }; while (!WaitHandle.WaitAll(waitHandles,1000)) { Console.WriteLine("Main thread working..."); Console.WriteLine("Main thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString()); Console.WriteLine(); } //結束異步委托,並獲取返回值 userInfoRes = myDelegate.EndInvoke(result); userInfoList.Add(userInfoRes); userInfoRes = myDelegate.EndInvoke(result1); userInfoList.Add(userInfoRes); } foreach (UserInfo user in userInfoList) { Console.WriteLine("My name is " + user.Name); Console.WriteLine("I'm " + user.Age + " years old this year"); Console.WriteLine("Thread ID is:" + user.ThreadId); } Console.ReadKey(); } } public class ThreadDemoClass { public UserInfo Run(UserInfo userInfo) { userInfo.ThreadId = Thread.CurrentThread.ManagedThreadId; Console.WriteLine("Child thread working..."); Console.WriteLine("Child thread ID is:" + userInfo.ThreadId); Console.WriteLine(); return userInfo; } }
執行結果:
WaitAll() 方法和 WaitAny() 方法都可以監視多個對象,不同的是 WaitAll() 方法需要等待所有的監視對象都收到信號之后才會返回 true,否則返回 false。而 WaitAny() 則是當有一個監視對象收到信號之后就會返回一個 int 值,這個 int 值代表的是當前收到信號的監視對象的索引。注意:在定義監視對象的時候,不能包含對同一個對象的多個引用,我這里是定義的兩個示例,所以是不同的對象。
接下來我們看上面的執行結果。咦?為什么主線程沒有“工作”呢?
這里你可以在異步委托調用的 Run() 方法里面為線程設置一個幾秒鍾或者更長的掛起時間。然后在設置監視對象這里設置一個小於線程掛起的時間,然后調試你就能發現問題的所在了。其實也不算是問題,只是之前的理解有誤。
其實 WaitAll() 方法和 WaitAny() 方法設置監視對象,然后指定一個時間(毫秒值),這里的意思是當所有的監視對象在指定的時間內都接收到信號時(這里是指 WaitAll() 方法),就不會執行 while 里面的主線程的工作,反之就會執行。
這里你可能會有疑問,如果是這樣,那我把前面的邏輯非運算符去掉那不就相反了么。這么理解邏輯上是沒錯的,但是我還是要說的是,盡量不要去嘗試,因為這會是個死循環。WaitAny() 這個方法也是一樣的理解,不同的是,它不需要等到所有的監視對象都收到信號,它只需要一個監視對象收到信號就夠了,這里就不在演示了。
上面的方法可以看出,我們雖然使用的是異步的方式調用的方法,但是依舊需要等待異步的方法返回執行的結果,盡管我們可以不阻塞主線程,但是還是覺得不太方便。所以也就有了本篇博客最后一個要點,異步委托的回調函數。
class Program { //定義一個委托類 private delegate UserInfo MyDelegate(UserInfo userInfo);
static void Main(string[] args) { ThreadDemoClass demoClass = new ThreadDemoClass(); UserInfo userInfo = null; //創建一個委托並綁定方法 MyDelegate myDelegate = new MyDelegate(demoClass.Run); //創建一個回調函數的委托 AsyncCallback asyncCallback = new AsyncCallback(Complete); for (int i = 0; i < 3; i++) { userInfo = new UserInfo(); userInfo.Name = "Brambling" + i.ToString(); userInfo.Age = 33 + i; //傳入參數並執行異步委托,並設置回調函數 IAsyncResult result = myDelegate.BeginInvoke(userInfo, asyncCallback, null); } Console.WriteLine("Main thread working..."); Console.WriteLine("Main thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString()); Console.WriteLine(); Console.ReadKey(); } public static void Complete(IAsyncResult result) { UserInfo userInfoRes = null; AsyncResult asyncResult = (AsyncResult)result; //獲取在其上調用異步調用的委托對象 MyDelegate myDelegate = (MyDelegate)asyncResult.AsyncDelegate; //結束在其上調用的異步委托,並獲取返回值 userInfoRes = myDelegate.EndInvoke(result); Console.WriteLine("My name is " + userInfoRes.Name); Console.WriteLine("I'm " + userInfoRes.Age + " years old this year"); Console.WriteLine("Thread ID is:" + userInfoRes.ThreadId); } } public class ThreadDemoClass { public UserInfo Run(UserInfo userInfo) { userInfo.ThreadId = Thread.CurrentThread.ManagedThreadId; Console.WriteLine("Child thread working..."); Console.WriteLine("Child thread ID is:" + userInfo.ThreadId); Console.WriteLine(); return userInfo; } }
執行結果:
從上面可以看到主線程再執行了異步委托之后繼續執行了下去,然后在回調函數里輸出了信息,也就是說在調用了異步委托之后就不管了,把之后的結束委托和獲取委托的返回值放到了回調函數中,因為回調函數是沒有返回值的,但是回調函數可以有一個參數。上面說到的 BeginInvoke() 方法的最后兩個參數,它的倒數第二個參數就是一個回調函數的委托,最后一個參數可以設置傳入回調函數的參數。如下:
class Program { //定義一個委托類 private delegate UserInfo MyDelegate(UserInfo userInfo); static List<UserInfo> userInfoList = new List<UserInfo>(); static void Main(string[] args) { ThreadDemoClass demoClass = new ThreadDemoClass(); UserInfo userInfo = null; //創建一個委托並綁定方法 MyDelegate myDelegate = new MyDelegate(demoClass.Run); //創建一個回調函數的委托 AsyncCallback asyncCallback = new AsyncCallback(Complete); //回調函數的參數 string str = "I'm the parameter of the callback function!"; for (int i = 0; i < 3; i++) { userInfo = new UserInfo(); userInfo.Name = "Brambling" + i.ToString(); userInfo.Age = 33 + i; //傳入參數並執行異步委托,並設置回調函數 IAsyncResult result = myDelegate.BeginInvoke(userInfo, asyncCallback, str); } Console.WriteLine("Main thread working..."); Console.WriteLine("Main thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString()); Console.WriteLine(); Console.ReadKey(); } public static void Complete(IAsyncResult result) { UserInfo userInfoRes = null; AsyncResult asyncResult = (AsyncResult)result; //獲取在其上調用異步調用的委托對象 MyDelegate myDelegate = (MyDelegate)asyncResult.AsyncDelegate; //結束在其上調用的異步委托,並獲取返回值 userInfoRes = myDelegate.EndInvoke(result); Console.WriteLine("My name is " + userInfoRes.Name); Console.WriteLine("I'm " + userInfoRes.Age + " years old this year"); Console.WriteLine("Thread ID is:" + userInfoRes.ThreadId); //獲取回調函數的參數 string str = result.AsyncState as string; Console.WriteLine(str); } } public class ThreadDemoClass { public UserInfo Run(UserInfo userInfo) { userInfo.ThreadId = Thread.CurrentThread.ManagedThreadId; Console.WriteLine("Child thread working..."); Console.WriteLine("Child thread ID is:" + userInfo.ThreadId); Console.WriteLine(); return userInfo; } }
執行結果:
回調函數的參數也是 object 類型的,我這里用的是一個 string 類型,但是它也可以是自定義類型的參數。
本篇博客到此結束,在寫這篇博客的同時也讓我個人對多線程編程加深了理解,多線程編程的知識點還有很多,后面再繼續與大家分享。
參考:
http://www.cnblogs.com/leslies2/archive/2012/02/07/2310495.html#t1