文章出處

前段時間, 拿到一個框架, 之前也沒怎么看, 只記得里面使用了蠻多的異步. 

public async Task<ActionResult> Login(LoginModel model, string returnUrl)

之前的項目中, 沒有使用過異步的. 可能有人會把多線程和異步混為一談, 其實還是不一樣的東西.

那么, 今天就先來學習下異步, 以備使用之需. 這里只介紹新的方式了, 至于之前老的方式, 有些復雜, 沒有新方式直觀, 簡潔.

一. 知識點

異步方法:提供了一種簡便方式完成可能需要長時間運行的工作,而不必阻止調用方的線程。 異步方法的調用方可以繼續工作,而不必等待異步方法完成。

await:運算符應用于一個異步方法的任務掛起方法的執行,直到等待任務完成。 任務表示正在進行的工作。 await 表達式不阻止它在其上執行的線程

async: async 修飾符指示方法、它進行修改 lambda 表達式或 匿名方法 是異步的

Task類:它表示一個任務,在.net4.5版本開始被支持, 它隸屬于 System.Threading.Tasks命名空間下;通過Task類可以方便的開啟一個新的線程。

 

二、小Demo

1.mvc文件下載

 在mvc源碼解析的時候, 不知道我有沒有提過, mvc在默認模式下, 是使用的異步方式來完成工作的. 那先來看一個mvc里面的應用吧

public class HomeController : Controller
{
    public async Task<FileResult> DownLoad(string name)
    {
        var path = Server.MapPath("~/FileRes/") + name;
        using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096, FileOptions.Asynchronous))
        {
            byte[] data = new byte[fs.Length];
            await fs.ReadAsync(data, 0, data.Length);
            return File(data, "application/octet-stream", name);
        }
    }
}

這里讀取文件, 是以異步的方式來讀取的, 當讀取完成之后, 就會將fs關掉, 所以在返回文件的時候, 并不能使用 return File(fs,"application/octet-stream",name)的方式去返回了. 

 

2. 同步方法調用異步方法, 以及異步方法調用異步方法

static void Main(string[] args)
{
    Console.WriteLine("Main Start : " + Thread.CurrentThread.ManagedThreadId);
    Step1();
    Step2();
    Console.WriteLine("Main End : " + Thread.CurrentThread.ManagedThreadId);
    Console.ReadKey();
}

static async void Step1()
{
    Console.WriteLine("Step1 start : " + Thread.CurrentThread.ManagedThreadId);
    try
    {
        await Task.Run(() => {
            Console.WriteLine("Step1.1 Current sleeping ThreadID : " + Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(3000);
        });

        await Task.Run(() =>
        {
            Console.WriteLine("Step1.2 Current sleeping ThreadID : " + Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(3000);
            Console.WriteLine("ThreadTest.Test Runing : " + Thread.CurrentThread.ManagedThreadId);
        });
    }
    catch (Exception ex)
    {
        Console.WriteLine("ThreadTest : " + ex.Message);
    }
    await Step3();
    Console.WriteLine("Step1 end : " +Thread.CurrentThread.ManagedThreadId);
}

static void Step2()
{
    Console.WriteLine("Step2 start : " + Thread.CurrentThread.ManagedThreadId);
    Console.WriteLine("Step2 Current ThreadID : " + Thread.CurrentThread.ManagedThreadId);
    Console.WriteLine("Step2 end : " + Thread.CurrentThread.ManagedThreadId);
}

static async Task Step3()
{
    Console.WriteLine("Step3 start : " + Thread.CurrentThread.ManagedThreadId);
    await Task.Run(() =>
    {
        Console.WriteLine("Step3 Current sleeping ThreadID : " + Thread.CurrentThread.ManagedThreadId);
        Thread.Sleep(5000);
    });
    Console.WriteLine("Step3 end : " + Thread.CurrentThread.ManagedThreadId);
}

Main方法調用方法Step2是同步方法調用同步方法, 調用Step1是同步方法調用異步方法.

Step1方法調用Step3方法, 是異步方法調用異步方法.

運行結果:

這里我運行了好幾次, 從上面的結果, 可以看出以下幾點:

1. 主線程全程沒有被阻塞, 一直執行到結束.

  如果我在Step2方法中, 加上一句 : Thread.Sleep(10), 那么主線程就會被阻塞在這里, 等待10s中的時間. 啥事都不用干的感覺, 真好.

2. 主線程在Step1方法中, 碰到await等待的時候, 完全沒理會, 直接回到Main方法中, 去執行Step2方法了.

  這里就是同步方法調用異步方法時要注意的地方, 因為同步方法不會理await的, 并不會乖乖的在這里等await后面的方法執行結束, 還有一點, await下面的方法, 主程序也不會去管了.

3. 注意到這里的Step1.1和Step1.2的執行線程, 有時候相同, 有時候不同. 這是為什么呢?

  這些線程應該都是從線程池中取的, 在異步方法中, 如果當前正在使用的線程已經不需要用了, 會把他還給線程池, 由線程池再分配出去, 給別的線程用. 當休息時間到了之后, 會再向線程池請求線程來完成下面的工作. 有點像打車, 到了一個地方之后, 下車了, 就把出租車這個資源釋放掉了, 等一個小時之后, 要回去了, 再打一個車, 這時候的出租車, 可能是之前打的那輛, 當然也可能不是. 這樣做的一個好處, 就是能增加吞吐量, 提升并發處理能力. 這里出租車如果一直在這里等你的話, 你是不需要付錢的, 賺的少了, 出租車可不怎么愿意了, 哈哈. 

 4. Step1調用Step3的時候, 很明顯的異步方法調用異步方法, 那么他會不會像之前的同步方法調用異步方法那樣呢?

  從上面的圖中, 就能很清晰的看到, 并不是一樣的. 效果上, 像同步方法調用同步方法那樣, 等在這里, 一直等你到天荒地老. 

猜想:

如果從計算機原理上來分析這里的異步掛起切換線程, 我猜想過程應該是這樣子的:

  當程序運行到sleep之前的時候, 會將數據什么的存入到存儲器和寄存器, 碰到sleep的時候, 就會釋放線程, 開啟定時器, 定時器到點觸發, 通知線程池, 我這邊需要一條線程來繼續處理, 此時線程池收到消息, 會去池中查看哪些線程可用, 如果都沒有, 會先等待一會, 可能是0.5s吧, 如果還沒有可用的線程釋放出來, 就會創建一條線程, 來滿足要求, 完成工作.

 

參考:

 詳解.NET異步


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()