多線程--await/async

1.await/async簡述

  • await/async是C#保留關鍵字,通常成對出現
  • async修飾方法,可以單獨出現,對原來的方法沒有任何改變,但會警告
  • await在方法體,只能出現在task/async方法前面,單獨使用await會報錯
  • async位於方法修飾符之後,返回類型之前

2.相關例子

2.1.單獨使用async

  • 代碼如下:
/// <summary>
///方法帶async,但方法體中Task前面不帶await(與普通方法執行一樣,只不過會警告)
/// </summary>
static async void AsyncNoAwait()
{
    Console.WriteLine($"AsyncNoAwait start ThreadId:{Thread.CurrentThread.ManagedThreadId}");

    Task.Run(() =>
    {
        Console.WriteLine($"AsyncNoAwait--Task start ThreadId:{Thread.CurrentThread.ManagedThreadId}");
        Thread.Sleep(1000);
        Console.WriteLine($"AsyncNoAwait--Task end ThreadId:{Thread.CurrentThread.ManagedThreadId}");
    });

    Console.WriteLine($"AsyncNoAwait end ThreadId:{Thread.CurrentThread.ManagedThreadId}");
}
  • 在控制檯輸出如下:

  • 結論:使用方式與普通方法無異,只是會有警告

2.2.await和async同時使用

await在方法內部位於Task前面,當前線程遇到await則跳出方法,然後繼續執行,而await Task.Run()之後的代碼都要等Task執行完成之後執行,效果與Task.Run()的回調一樣

  • 代碼如下:
static void AsyncAwaitTest()
{
    Console.WriteLine($"AsyncAwaitTest start ThreadId:{Thread.CurrentThread.ManagedThreadId}");

    //線程執行到AsyncWithAwait方法裏面的await語句之後,跳出此方法,繼續之後後面的代碼
    AsyncWithAwait(); 

    Console.WriteLine($"AsyncAwaitTest end ThreadId:{Thread.CurrentThread.ManagedThreadId}");
}


/// <summary>
/// await/async同時使用
/// 此await的作用就是讓當前線程不執行後面的代碼,而是跳出方法體,去執行調用次方法後面的代碼,
/// 而await後面的代碼在await 異步任務執行完成之後調用,類似回調
/// </summary>
static async void AsyncWithAwait()
{
    Console.WriteLine($"AsyncWithAwait start ThreadId:{Thread.CurrentThread.ManagedThreadId}");

    //執行到此處,當前線程就會跳出當前方法
    await Task.Run(() =>
    {
        Console.WriteLine($"AsyncWithAwait--Task start ThreadId:{Thread.CurrentThread.ManagedThreadId}");
        Thread.Sleep(1000);
        Console.WriteLine($"AsyncWithAwait--Task end ThreadId:{Thread.CurrentThread.ManagedThreadId}");
    });

    //此處的輸出等上面的Task.Run 執行結束之後執行
    Console.WriteLine($"AsyncWithAwait end ThreadId:{Thread.CurrentThread.ManagedThreadId}");
}
  • 控制檯輸出如下:

  • 結論:當前線程1執行遇到await,不執行await後面的代碼,而await Task.Run()新開一個線程3執行lambda,所以之後兩個線程是併發執行的,互不干擾,只不過是先後順序而已,至於Task.Run()後面的代碼,是在Task.Run()執行完成之後執行的,可能會是當前線程也可能會新開一個線程

2.3.async方法上加多Task

  • 代碼如下:
static void AsyncAwaitTest()
{
    Console.WriteLine($"AsyncAwaitTest start ThreadId:{Thread.CurrentThread.ManagedThreadId}");

    //線程執行到AsyncWithAwait方法裏面的await語句之後,跳出此方法,繼續之後後面的代碼
    Task task = TaskAsyncNoRerturn(); 
    //wait會阻塞當前線程,直到TaskAsyncNoRerturn方法中await task後面的同步方法執行完成
    task.Wait();

    Console.WriteLine($"AsyncAwaitTest end ThreadId:{Thread.CurrentThread.ManagedThreadId}");
}

/// <summary>
 /// 方法帶Task,但方法沒有返回值
 /// 返回的Task包含了整個調用的方法,如果使用Task.wait(),將等待方法同步線程執行完畢
 /// </summary>
 /// <returns></returns>
 static async Task TaskAsyncNoRerturn()
 {
     Console.WriteLine($"TaskAsyncNoRerturn start ThreadId:{Thread.CurrentThread.ManagedThreadId}");

     Task task = Task.Run(() =>
     {
         Console.WriteLine($"TaskAsyncNoRerturn--Task start ThreadId:{Thread.CurrentThread.ManagedThreadId}");
         Thread.Sleep(1000);
         Console.WriteLine($"TaskAsyncNoRerturn--Task end ThreadId:{Thread.CurrentThread.ManagedThreadId}");
     });

     await task; //方法聲明中的Task與當前task沒有任何關係

    //調用此方法,如果使用返回值task.wait()阻塞線程,將等到後面同步代碼執行完畢纔會跳過task.wait()
    for (int i = 0; i < 2; i++)
    {
        Console.WriteLine($"{i} ThreadId:{Thread.CurrentThread.ManagedThreadId}");
    Thread.Sleep(1000);
    }


    Console.WriteLine($"TaskAsyncNoRerturn end ThreadId:{Thread.CurrentThread.ManagedThreadId}");
}
  • 控制檯輸出如下:

  • 結論:在async方法中加入Task並不是指方法需要return,而是調用之後會自動返回一個Task類型的對象(暫時作爲 task),調用task.wait()之後,會阻塞當前線程,直到async方法內部的同步代碼執行完成

備註:async方法內部的同步代碼:指的是在await Task.Run()之後的代碼是沒有再創建任何線程去執行的代碼

2.4.帶返回值的async方法

方法聲明:訪問修飾符 async Task<返回值類型> 方法名

例子:public async Task<int> Method

  • 代碼如下:
static void AsyncAwaitTest()
{
    Console.WriteLine($"AsyncAwaitTest start ThreadId:{Thread.CurrentThread.ManagedThreadId}");

    Task<int> task = TaskAsyncWithRerturn();
    //調用task.Result獲取返回值,會阻塞當前線程,指定返回值計算完成
    Console.WriteLine($"TaskAsyncWithRerturn的返回值:{task.Result}");

    Console.WriteLine($"AsyncAwaitTest end ThreadId:{Thread.CurrentThread.ManagedThreadId}");
}

/// <summary>
/// TaskAsyncWithRerturn方法返回int類型的值
/// 調用TaskAsyncWithRerturn返回,會返回一個Task<int>對象
/// 調用者想要獲取返回值,就得等task線程計算完畢,就是會阻塞調用線程 
/// </summary>
/// <returns></returns>
static async Task<int> TaskAsyncWithRerturn()
{
    Console.WriteLine($"TaskAsyncWithRerturn start ThreadId:{Thread.CurrentThread.ManagedThreadId}");

    int result = 0;

    Task task = Task.Run(() =>
    {
        Console.WriteLine($"TaskAsyncWithRerturn--Task start ThreadId:{Thread.CurrentThread.ManagedThreadId}");
        for (int i = 0; i < 200; i++)
        {
            result += i;
            Thread.Sleep(10);
        }
        Console.WriteLine($"TaskAsyncWithRerturn--Task end ThreadId:{Thread.CurrentThread.ManagedThreadId}");
    });
    await task;

    Console.WriteLine($"TaskAsyncWithRerturn end ThreadId:{Thread.CurrentThread.ManagedThreadId}");

    return result;
}
  • 控制檯輸出如下:

  • 結論:調用帶返回值的async方法,如果要獲取其返回值,就會阻塞當前線程,直到async方法中的返回值計算完畢

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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