C#入門04:必點菜-線程和異步

線程

C#線程的生命週期分爲幾個階段:
1、未啓動狀態:當線程實例被創建但 Start 方法未被調用時的狀況;
2、就緒狀態:當線程準備好運行並等待 CPU 週期時的狀況;
3、不可運行狀態:下面的幾種情況下線程是不可運行的:
1). 已經調用 Sleep 方法;
2). 已經調用 Wait 方法;
3). 通過 I/O 操作阻塞。
4、死亡狀態:當線程已完成執行或已中止時的狀況。

一個簡單的不帶參數的線程:

    //線程函數
    public static void Task()
    {
        for (int i = 0; i < 10; i++)
        {
            Console.WriteLine("Task Print {0}", i);
            Thread.Sleep(1000);
        }
    }

    //創建簡單的線程,不帶任何參數
    Thread th = new Thread(Task);
    th.Name = "PrintThread";

    //啓動執行線程
    th.Start();

    //等待線程執行完畢
    th.Join();

    Console.WriteLine("Thread [{0}] Finished !", th.Name);

對於帶參數的線程方法,通常需要自定義一個線程參數類,示例:

    //自定義的線程參數類
    class ThreadParameter
    {
        private int size;

        public ThreadParameter(int s)
        {
            size = s;
        }

        //線程方法
        public void ThreadMethod()
        {
            for (int i = 0; i < size; i++)
            {
                Console.WriteLine("Task1 Print {0}", i);
                Thread.Sleep(1000);
            }
        }
    }

    //實例化線程參數類
    ThreadParameter tp = new ThreadParameter(10);

    //實例化線程對象
    Thread th = new Thread(tp.ThreadMethod);
    th.Name = "PrintThread";

    //啓動線程
    th.Start();

    //等待線程結束
    th.Join();

    Console.WriteLine("Thread [{0}] Finished !", th.Name);

Abort() 方法用於銷燬線程。通過拋出 threadabortexception 在運行時中止線程。這個異常不能被捕獲,如果有 finally 塊,控制會被送至 finally 塊。

    public static void ThreadProc()
    {
        try
        {
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine("Task1 Print {0}", i);
                Thread.Sleep(1000);
            }
        }
        //捕獲線程異常退出
        catch (ThreadAbortException e)
        {
            Console.WriteLine(e);
        }
        finally
        {
            Console.WriteLine("Thread Finished !");
        }
    }

    //實例化線程對象
    Thread th = new Thread(ThreadProc);

    //啓動線程
    th.Start();

    //等待5秒後結束線程
    Thread.Sleep(5000);
    th.Abort();

    Console.WriteLine("Thread [{0}] Finished !", th.Name);

異步

有時候一個函數的執行很長,不能一直等待函數執行完畢,希望在函數執行的時候主線程可以完成些其他的事情,然後再等待線程執行完畢。下面的代碼同步執行時會依次打印1~5數字,而異步執行的時候則是亂序的:

    static void Test(string name)
    {
        Console.WriteLine("TestMethod: {0:yyyy-MM-dd HH:mm:ss.fff} {1}", DateTime.Now, name);
    }

    static void Main()
    {
        TestDelegate d = Test;
        Console.WriteLine("Beginning : {0:yyyy-MM-dd HH:mm:ss.fff}", DateTime.Now);

        //同步執行,依次打印
        d("111");
        d("222");
        d("333");
        d("444");
        d("555");

        //異步執行,會放入線程池中,打印亂序
        var r1 = d.BeginInvoke("小明1", null, null);
        var r2 = d.BeginInvoke("小明2", null, null);
        var r3 = d.BeginInvoke("小明3", null, null);
        var r4 = d.BeginInvoke("小明4", null, null);
        var r5 = d.BeginInvoke("小明5", null, null);

        //此時異步執行還沒有完成
        Console.WriteLine("End       : {0:yyyy-MM-dd HH:mm:ss.fff}", DateTime.Now);

        //等待異步執行完成
        d.EndInvoke(r1);
        d.EndInvoke(r2);
        d.EndInvoke(r3);
        d.EndInvoke(r4);
        d.EndInvoke(r5);

        Console.WriteLine("線程全部執行完成");
            
        Console.Read();
    }

另外一個定時器定時打印的例子:

    //打印當前日期和時間
    static void PrintPoint(object sender, ElapsedEventArgs e)
    {
        DateTime dtCurr = DateTime.Now;
        Console.WriteLine("{0:yyyy-MM-dd HH:mm:ss.fff}", dtCurr);
        Thread.Sleep(1000);
    }

    static void Main()
    {
        //構建定時器,
        System.Timers.Timer timer = new System.Timers.Timer();
        timer.Interval = 1000;
        timer.Start();

        //連接打印方式到定時器超時事件
        timer.Elapsed += new ElapsedEventHandler(PrintPoint);

        Console.WriteLine("End");
        Console.Read();
    }

在主線程中使用EndInvoke還是很麻煩,始終要阻塞等待。使用異步回調就可以解決這個問題,當異步返回時自動調用回調函數:

    //異步結束時的回調
    public static void BackCall(IAsyncResult parameter)
    {
        //parameter.IsCompleted用於判斷異步方法是否已調用完成;
        if (parameter.IsCompleted)
        {
            //通過EndInvoke方法獲取異步方法的返回結果(類型與異步方法的結果一致)      
            Console.Write(string.Format("回調完成,返回值"));
        }
        else
        {
            Console.Write("調用未完成");
        }
    }

    TestDelegate td = PrintPoint;
    td.BeginInvoke(BackCall, null);

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