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

線程

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

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

//線程函數
    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 塊。code

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數字,而異步執行的時候則是亂序的:orm

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();
相關文章
相關標籤/搜索