c#的ThreadPool使用筆記

摘要:
系列文章,從一個基本的代碼說起,逐步探索 ThreadPool 的奧妙。

首先,看看線程池的樣子:

從上圖看出,線程池維護1個至n個線程,操作系統從請求隊列中提取請求分配個線程池中的適合線程處理。

先寫下如下的代碼:

using System;
using System.Threading;

public class ThreadBase
{
    public static void Main ( ) {

        System.Threading.WaitCallback waitCallback = new WaitCallback ( MyThreadWork );

        ThreadPool.QueueUserWorkItem ( waitCallback, "第一個線程" );
        ThreadPool.QueueUserWorkItem ( waitCallback, "第二個線程" );
        ThreadPool.QueueUserWorkItem ( waitCallback, "第三個線程" );
        ThreadPool.QueueUserWorkItem ( waitCallback, "第四個線程" );
        Console.ReadLine ( );
    }

    public static void MyThreadWork ( object state ) {
        Console.WriteLine ( "線程現在開始啓動…… {0}",(string)state );
        Thread.Sleep ( 10000 );
        Console.WriteLine ( "運行結束…… {0}",( string ) state );
    }
}
分析上面的代碼:
一、首先定義了一個 System.Threading.WaitCallback 對象 waitCallback。 
WaitCallback 是一個委託,表示線程池線程要執行的回調方法,它的原型如下:
[ComVisibleAttribute(true)] 
public delegate void WaitCallback (Object state)


1、這裏有一個 Callback 機制的問題,所謂的Callback 簡單的理解就是由操作系統調用的函數,我們自己寫的程序不需要調用。就像有個修理工到家裏搞修理,你只需要告訴他鉗子、螺絲刀、膠布等修理工具在什麼地方就行,而不要管他在什麼時候、在什麼地方怎樣使用這些修理工具。
2、WaitCallback 的參數" Object state",這個參數包含回調方法要使用的信息的對象。在接下來的情況中我再說。
既然 WaitCallback 委託的原型如此,那麼我們就申明一個跟它的樣子差不多的函數,這個函數就是要線程做的事情。

public static  void MyThreadWork ( object state )


這裏函數中多了一個 "static",這是因爲Main的關係(更大一點就是因爲C#語言機制的問題),如果WaitCallback 的對象不是在靜態(static)方法中,這個static 是不需要的。

二、接着就是要執行的方法放入線程池中,以便操作系統執行。
我這裏放置了四個方法要操作系統執行:

        ThreadPool.QueueUserWorkItem ( waitCallback, "第一個線程" );
        ThreadPool.QueueUserWorkItem ( waitCallback, "第二個線程" );
        ThreadPool.QueueUserWorkItem ( waitCallback, "第三個線程" );
        ThreadPool.QueueUserWorkItem ( waitCallback, "第四個線程" );


這裏,我放置到線程池中的操作是一樣的,當然也可以不一樣,接下來說。

三、最後阻塞主線程,等待線程池中的線程執行

 Console.ReadLine ( );


如果忽略掉這個代碼,則有可能看不到任何輸出

好了,這是主線程做的事情了,接下來看看線程池中的線程做的事情。
這個很簡單,就是將線程的參數輸出,然後線程睡眠(sleep)一段時間,最後輸出線程結束的信息。

 

在上次的基礎上,我需要做以下的事情:
1、要傳遞給線程的參數更加複雜;
2、線程要處理的工作更加多樣;
3、在線程中我要改變主線程傳入的參數,並告訴主程序。

好的,第一個問題,因爲 WaitCallback 委託的原型決定了參數只能有一個 ( Object state ),那沒有辦法,我們只能將多個參數封裝到一個Object 中,也就是 class 中。
第二個問題,要處理這個問題也很簡單,就是再定義一個 WaitCallback 委託的對象,將它作爲參數傳遞給ThreadPool.QueueUserWorkItem ()方法。這個對象可以在線程裏面處理另外一類的工作。
代碼如下:

using System;
using System.Threading;

public class App
{
    public static void Main ( ) {
        WaitCallback waitCallback = new WaitCallback ( MyThreadWork );

        WaitCallback waitCallback2= new WaitCallback ( MyThreadWork2 );     // 增加線程工作類型

        ThreadPool.QueueUserWorkItem ( waitCallback, "第一個線程" );
        ThreadPool.QueueUserWorkItem ( waitCallback, "第二個線程" );

        MyState myState = new MyState ( "第三個線程",100);   // 增加自定義的線程參數類型
        ThreadPool.QueueUserWorkItem ( waitCallback2, myState );
        ThreadPool.QueueUserWorkItem ( waitCallback2, new MyState("第四個線程",2) );

        Console.WriteLine ( "MyState 的 Number 值爲: {0}", myState.Number );    // 讀取線程改變後的 MyState
        Console.ReadLine ( );
    }

    public static void MyThreadWork ( object state ) {
        Console.WriteLine ( "MyThreadWork 開始啓動 …… {0}", ( string ) state );
        Thread.Sleep ( 10000 );
        Console.WriteLine ( "運行結束…… {0}", ( string ) state );
    }

    // use MyState class 
    public static void MyThreadWork2 ( object state ) {
        Console.WriteLine ( "MyThreadWork2 開始啓動…… {0},{1}", ( ( MyState ) state ).Info, ( ( MyState ) state ).Number );
        Thread.Sleep ( 10000 );
        ( ( MyState ) state ).Number += 1;      // 將 state的 Number 加 1
        Console.WriteLine ( "運行結束…… {0},{1}", ( ( MyState ) state ).Info, ( ( MyState ) state ).Number );
    }
}

public class MyState
{
    private string info;
    private int number;


    public MyState ( string info, int number ) {
        this.info = info;
        this.number = number;
    }

    public string Info {
        get {
            return this.info;
        }
        set {
            this.info = value;
        }
    }

    public int Number {
        get {
            return this.number;
        }
        set {
            this.number = value;
        }
    }
}


在代碼裏面,我嘗試對三個問題都進行解決,但是,很遺憾,上面的代碼只是解決了第1、2個問題。
雖然,我的MyThreadWork2 () 線程嘗試對 MyState 的 Number 進行加1操作,但是,主線程的輸出仍然可能是 100,如下所示:

MyState 的 Number 值爲: 100
MyThreadWork 開始啓動 …… 第一個線程
MyThreadWork 開始啓動 …… 第二個線程
MyThreadWork2 開始啓動…… 第三個線程,100
MyThreadWork2 開始啓動…… 第四個線程,2
運行結束…… 第一個線程
運行結束…… 第二個線程
運行結束…… 第三個線程,101
運行結束…… 第四個線程,3


光從代碼看,我們的輸出 MyState 信息的代碼應該是後面執行,想不到它居然在線程啓動之前就執行了!
呵呵,這就是多線程!
看樣子,我需要控制我的主線程——等所有的線程池中的線程都執行完成後,才接着執行主線程中輸出 MyState 信息的代碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章