1. 程式人生 > >C# 多線程系列(三)

C# 多線程系列(三)

job row 空閑 最好 方式 不同的 運行時 作業 tun

線程池

創建線程需要時間,如果有不同的小任務要完成,就可以事先創建許多線程,在應完成這些任務時發出請求。這個線程數最好在需要更多線程時增加,在需要釋放資源時減少。

不需要自己創建這樣的一個列表。該列表由ThreadPool類托管。該類會在需要時增加線程池中線程數,直到最大的線程數。

  • 可以指定創建線程池時立即啟動的最小線程數,以及線程池中可用的最大線程數。
  • 如果更多的作業要處理,線程池中的線程個數也到了極限,最新的作業就要排隊,且必須等待線程完成其作業。
  • 線程池中的線程都是後臺線程,不能把入池的線程改為前臺線程。
  • 不能給入池的線程設置優先級和名字。
  • 對於COM對象,入池的所有線程都是多線程單元線程。許多COM對象都需要單線程單元線程。
  • 入池的線程只能用於時間比較短的任務。如果線程要一直運行,就應該使用Thread類創建一個線程。

static void Main()
{
    int nWorkerThreads;
    int nCompletionPorThreads;
    ThreadPool.GetMaxThreads(out nWorkerThreads, out nCompletionPorThreads);
    //ThreadPool.SetMaxThreads(500, 500);
    lib.print("Max worker threads : " + nWorkerThreads);
    lib.print(
"I/O completion threads: " + nCompletionPorThreads); for(int i=0; i<10; i++) { ThreadPool.QueueUserWorkItem(JobForAThread);//將方法排入隊列以便執行。 此方法在有線程池線程變得可用時執行。 } Thread.Sleep(3000); } static void JobForAThread(object state) { for(int i = 0; i<3; i++) { Console.WriteLine(
"loop {0}, running inside pooled thread {1}", i, Thread.CurrentThread.ManagedThreadId); Thread.Sleep(50); } }

示例應用程序首先要讀取工作線程和I/O程的最大線程數,把這些信息寫入控制臺中。接著在for循環中,調用Thread.QueueuserWorkItm()方法,遞一個WaitCallback型的委托,把JobForAThread()方法賦予線程池中的線程。

線程池收到這個請求後,會從池中選擇一個線程,來調用該方法。如果線程池還沒有運行,會創建一個線程池,啟動第一個線程。如果線程池己經在運行,有一個空閑線程來完成該任務,把該作業傳遞給這個線程。

異步委托

創建線程最簡單的方式是定義一個委托,然後異步調用它。

委托使用線程池完成異步任務,當沒有前臺線程運行時,異步委托將直接結束。

static void Main(string[] args)
{
    Action action = new Action(() => 
    {
        for(int i=0; i<100; i++)
        {
            lib.put(".");
            Thread.Sleep(10);
        }
    });

    AsyncCallback calback = (IAsyncResult result) =>
    {
        Thread.Sleep(100);
        lib.print(result.AsyncState);
    };
    var rs = action.BeginInvoke(calback, "Begin Invoke");//如此,便啟動了異步委托。下面用不同方法等待異步委托完成。


    //方法一,用EndInvoke方法,該方法會一直等待,直到委托完成任務為止。
    action.EndInvoke(rs);

    //方法二,使用IAsyncResult相關聯的等待句柄。使用AsyncWaitHandle屬性可以訪問等待句柄。
    //這個屬性返回一個WaitHandle類型對象,它可以等待委托線程完成其任務。參數是最長等待時間,
    //-1表示無限等待。如果當前實例收到信號,則返回為 true;否則為 false。
    rs.AsyncWaitHandle.WaitOne(5000);

    //方法三,不斷檢查
    while (true)
    {
        if (!rs.IsCompleted)
        {
            Thread.Sleep(50);
        }
        else
        {
            break;
        }
    }

    //因為callback最終是異步線程回調的,所以,如果直接退出,callback將無法打印出Begin Invoke。
    Thread.Sleep(200);
}

任務

System.Threading.Tasks包含的類抽象出了線程功能,在後臺使用ThreadPool。任務表示應完成的某個單元工作,這個單元工作可以在單獨的線程中運行,也可以以同步方式啟動一個任務,這需要等待主線程。使用任務不僅可以獲得一個抽象層,還可以對底層線程進行很多控制。

  • 啟動任務

可以使用實例化的TaskFactory類,在其中把TaskMethod()方法傳遞給StarNew()方法,就會立刻啟動任務。也可以使用Task類的構造函數。實例化Task對象時,任務不會立即運行,而是指定Created狀態。接著調用Task類的Start()方法,來啟動任務。使用Task類時,還可以調用TunSynchronously()方法。

static void Main(string[] args)
{
    Task t1 = new Task(DoOnFirst);
    t1.Start();

    TaskFactory tf = new TaskFactory();
    Task t2 = tf.StartNew(DoOnFirst);

    Task t3 = Task.Factory.StartNew(DoOnFirst);

    Task.WaitAll(t1, t2, t3);
}
static void DoOnFirst()
{
    lib.print("Task.CurrentId :" + Task.CurrentId);
    lib.print("-----------");
    Thread.Sleep(1000);
}

  • 連續的任務

連續任務通過在任務上調用ContinueWith()方法類定義。不帶TaskContinuationOptions參數,則無論前一個任務是如何結束的,後續任務都啟動。也可以用TaskContinuationOptions枚舉中的值,來指定連續任務在什麽情況下啟動。

static object o = new object(); static void Main(string[] args) { CancellationTokenSource cs = new CancellationTokenSource(); Task t1 = new Task(DoOnFirst, cs.Token); Task t2 = t1.ContinueWith(DoOnSecond, TaskContinuationOptions.OnlyOnRanToCompletion);//t1完成的情況下啟動t2 Task t3 = t1.ContinueWith(DoOnThird, TaskContinuationOptions.OnlyOnCanceled);//t1被取消的情況下啟動t3 try { t1.Start(); //cs.Cancel(); //打開註釋,取消了t1,將執行DoOnThird。 //cs.Token.ThrowIfCancellationRequested(); } catch { lib.print("t1.IsCanceled : " + t1.IsCanceled); } Console.ReadKey(); } static void DoOnFirst() { lock (o) { lib.print("Task.CurrentId :" + Task.CurrentId); lib.print("-----------"); Thread.Sleep(1000); } } static void DoOnSecond(Task t) { lock (o) { lib.print("task " + t.Id + " finished."); lib.print("this task id " + Task.CurrentId); lib.print("-----------"); Thread.Sleep(1000); } } static void DoOnThird(Task t) { lock (o) { lib.print("task " + t.Id + " Canceld."); lib.print("this task id " + Task.CurrentId); lib.print("-----------"); Thread.Sleep(1000); } }

任務層次結構

任務也可以構成一個層次結構。一個任務啟動一個新任務時,就啟動了一個父/子層次結構。

任務的結果

任務結束時,可以把一些有用的狀態信息寫到共享對象中。也可以使用返回結果的任務返回這些信息。

static void Main(string[] args)
{
    Task<int> t1 = new Task<int>((object o)=>{return 111;}, "");
    t1.Start();
    t1.Wait();
    lib.print(t1.Result);

    Task t = new Task(DoParentTask);
    t.Start();
}

C# 多線程系列(三)