1. 程式人生 > >Async(異步)(一)

Async(異步)(一)

正在 簡單 同步 屬性 logs 一般來說 his 毫秒 一個

在談到異步的概念時,先要了解幾個概念了。

什麽是進程?

當一個程序開始運行時,它就是一個進程,進程包括運行中的程序和程序所使用到的內存和系統資源。

而一個進程又是由多個線程所組成的

什麽是線程?

線程是程序中的一個執行流,每個線程都有自己的專有寄存器(棧指針、程序計數器等),但代碼區是共享的,即不同的線程可以執行同樣的函數。


什麽是多線程?

多線程是指程序中包含多個執行流,即在一個程序中可以同時運行多個不同的線程來執行不同的任務,也就是說允許單個程序創建多個並行執行的線程來完成各自的任務。

了解完進程、線程、多線程後,再來了解同步和異步

同步方法調用在程序繼續執行之前需要等待同步方法執行完畢返回結果,簡單來說就是按照方法的執行順序從上向下一次執行。
異步方法則在被調用之後立即返回以便程序在被調用方法完成其任務的同時執行其它操作。

舉個例子:比如張三叫你吃飯,你說有點事需要完成後再去吃飯,而張三等你做完了事之後,再和你一起去吃飯。這就是同步

或者張三叫你吃飯,你說有點事需要完成後再去吃飯,但是張三就只是叫了下你吃飯,他也沒有等你回答,就自己獨自去吃了。這就可以是異步。

那麽在程序中如何聲明一個同步,異步呢,其實提到同步異步,就必須熟悉委托,不熟悉的可以看前面的博客,在這裏就不多闡述了。

首先聲明一個委托和一個普通方法

         private delegate void DoSomethingDelegate(string name);
 


         Console.WriteLine("****************btnAsync_Click Start {0}***************", Thread.CurrentThread.ManagedThreadId);


         DoSomethingDelegate method = new DoSomethingDelegate(this.DoSomethingLong);

      method.Invoke("123");

Console.WriteLine("****************btnAsync_Click End {0}***************", Thread.CurrentThread.ManagedThreadId); private void DoSomethingLong(string name) { Console.WriteLine("****************DoSomethingLong Start {0}***************", Thread.CurrentThread.ManagedThreadId); long lResult = 0; for (int i = 0; i < 10000000; i++) { lResult += i; } Thread.Sleep(2000); Console.WriteLine("****************DoSomethingLong End {0}***************", Thread.CurrentThread.ManagedThreadId);

 技術分享

可以看出程序的運行程序是從上向下執行的,這就是同步。

但是如果是異步調用的情況,就是另外一種情況

method.BeginInvoke("123", null, null);

技術分享

你會發現異步時,程序不管你調用的方法有沒有執行,它直接往下執行,而異步調用的方法開啟了新線程,在後面繼續執行。

那麽上面BeginInvoke中兩個null參數代表著什麽呢。

技術分享

簡單來說就是一個是IAsyncResult 類型的委托,和一個動態參數(而一般來說最後一個參數一般當做狀態參數來使用)。

舉個例子

            IAsyncResult asyncResult = null;

            AsyncCallback callback = t =>
                {
                    Console.WriteLine(t.Equals(asyncResult));

                    Console.WriteLine(t.AsyncState);
                    Console.WriteLine("這裏是回調函數 {0}", Thread.CurrentThread.ManagedThreadId);
                };

      

            asyncResult = method.BeginInvoke("123", callback, "ming");

 技術分享

通過例子也可以得出 t.AsyncState其實和你BeginInvoke的第三個參數是相等的。其實這個例子還可以得出,method.BeginInvoke()得到的返回值是和BeginInvoke()第二個參數是相等的。

但是,假如有個這樣的需求,等待異步結束後,主線程還要做事兒,通過前面的例子可以得出,單單使用BeginInvoke()是做不到的,在這裏介紹幾種方式:

1)IsCompleted屬性,他會判斷異步是否完成,在這裏就可以加一個判斷。

            int i = 1;
            while (!asyncResult.IsCompleted)
            {
                Console.WriteLine("*****正在計算,已完成{0}%。。。。", 10 * i++);
                Thread.Sleep(100);
            }

  技術分享

這樣他會等異步結束後跳出循環,再繼續執行主線程。但是這樣的做法會影響一點效率,因為它不是在異步結束的第一時間就跳出循環,會有一個sleep時間。

2)WaitOne()方法

  asyncResult.AsyncWaitHandle.WaitOne();//一直等待
  asyncResult.AsyncWaitHandle.WaitOne(-1);//一直等待
  asyncResult.AsyncWaitHandle.WaitOne(1000);//等待1000毫秒,超時就不等待了

3)EndInvoke()方法

  method.EndInvoke(asyncResult);

其實EndInvoke()除了會等待線程之外,他還可以接受一個返回值輸出

假設有一個帶string類型返回值的委托,如果直接invoke也可以得到這個返回值

   Func<int, string> func1 = i =>
                {
                    DoSomethingLong("btnAsync_Click");
                    return "二零一七給力";
                };
string s = func1.Invoke(123);

但是你需要開啟一個新的線程,而用BeginInvoke是不能得到返回值的,因為它只能返回的是IAsyncResult類型。

這個時候就需要EndInvoke,來實現這個需求

string sResult = func1.EndInvoke(asyncResult)

總的來說這三種方式各有各的優勢,需要根據實際情況來使用。

未完待續。。。

Async(異步)(一)