中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C#中異步與多線程的作用有哪些

發布時間:2021-01-18 15:18:32 來源:億速云 閱讀:154 作者:Leah 欄目:開發技術

C#中異步與多線程的作用有哪些?針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

UI線程

還有一件重要的事情需要知道的是為什么使用這些工具是好的。在.net中,有一個主線程叫做UI線程,它負責更新屏幕的所有可視部分。默認情況下,這是一切運行的地方。當你點擊一個按鈕,你想看到按鈕被短暫地按下,然后返回,這是UI線程的責任。你的應用中只有一個UI線程,這意味著如果你的UI線程忙著做繁重的計算或等待網絡請求之類的事情,那么它不能更新你在屏幕上看到的東西,直到它完成。結果是,你的應用程序看起來像“凍結”——你可以點擊一個按鈕,但似乎什么都不會發生,因為UI線程正在忙著做其他事情。

理想情況下,你希望UI線程盡可能地空閑,這樣你的應用程序似乎總是在響應用戶的操作。這就是異步和多線程的由來。通過使用這些工具,可以確保在其他地方完成繁重的工作,UI線程保持良好和響應性。

現在讓我們看看如何在c#中使用這些工具。

C#的異步操作

執行異步操作的代碼非常簡單。你應該知道兩個主要的關鍵字:“async”和“await”,所以人們通常將其稱為async/await。假設你現在有這樣的代碼:

public void Loopy()
{
  var hugeFiles = new string[] {
   "Gr8Gonzos_Home_Movie_In_8k_Res.mkv", // 1 GB
   "War_And_Peace_In_150_Languages.rtf", // 1.2 GB
   "Cats_On_Catnip.mpg"         // 0.9 GB
  };

  foreach (var hugeFile in hugeFiles)
  {
    ReadAHugeFile(hugeFile);
  }
  
  MessageBox.Show("All done!");
}


public byte[] ReadAHugeFile(string bigFile)
{
  var fileSize = new FileInfo(bigFile).Length; // Get the file size
  var allData = new byte[fileSize];      // Allocate a byte array as large as our file
  using (var fs = new System.IO.FileStream(bigFile, FileMode.Open))
  {
    fs.Read(allData, 0, (int)fileSize);   // Read the entire file...
  }
  return allData;               // ...and return those bytes!
}

在當前的形式中,這些都是同步運行的。如果你點擊一個按鈕從UI線程運行Loopy(),那么應用程序將似乎凍結,直到所有三大文件閱讀,因為每個“ReadAHugeFile”是要花很長時間在UI線程上運行,并將同步閱讀。這可不好!讓我們看看能否將ReadAHugeFile變為異步的這樣UI線程就能繼續處理其他東西。

無論何時,只要有支持異步的命令,微軟通常會給我們同步和異步版本的這些命令。在上面的代碼中,System.IO.FileStream對象同時具有"Read"和"ReadAsync"方法。所以第一步就是將“fs.Read”修改成“fs.ReadAsync”。

public byte[] ReadAHugeFile(string bigFile)
{
  var fileSize = new FileInfo(bigFile).Length; // Get the file size
  var allData = new byte[fileSize];      // Allocate a byte array as large as our file
  using (var fs = new System.IO.FileStream(bigFile, FileMode.Open))
  {
    fs.ReadAsync(allData, 0, (int)fileSize); // Read the entire file asynchronously...
  }
  return allData;               // ...and return those bytes!
}

如果現在運行它,它會立即返回,并且“allData”字節數組中不會有任何數據。為什么?

這是因為ReadAsync是開始讀取并返回一個任務對象,這有點像一個書簽。這是.net的一個“Promise”,一旦異步活動完成(例如從硬盤讀取數據),它將返回結果,任務對象可以用來訪問結果。但如果我們對這個任務不做任何事情,那么系統就會立即繼續到下一行代碼,也就是我們的"return allData"行,它會返回一個尚未填滿數據的數組。

因此,告訴代碼等待結果是很有用的(但這樣一來,原始線程可以在此期間繼續做其他事情)。為了做到這一點,我們使用了一個"awaiter",它就像在async調用之前添加單詞"await"一樣簡單:

public byte[] ReadAHugeFile(string bigFile)
{
  var fileSize = new FileInfo(bigFile).Length; // Get the file size
  var allData = new byte[fileSize];      // Allocate a byte array as large as our file
  using (var fs = new System.IO.FileStream(bigFile, FileMode.Open))
  {
    await fs.ReadAsync(allData, 0, (int)fileSize); // Read the entire file asynchronously...
  }
  return allData;               // ...and return those bytes!
}

如果現在運行它,它會立即返回,并且“allData”字節數組中不會有任何數據。為什么?

這是因為ReadAsync是開始讀取并返回一個任務對象,這有點像一個書簽。這是.net的一個“Promise”,一旦異步活動完成(例如從硬盤讀取數據),它將返回結果,任務對象可以用來訪問結果。但如果我們對這個任務不做任何事情,那么系統就會立即繼續到下一行代碼,也就是我們的"return allData"行,它會返回一個尚未填滿數據的數組。

因此,告訴代碼等待結果是很有用的(但這樣一來,原始線程可以在此期間繼續做其他事情)。為了做到這一點,我們使用了一個"awaiter",它就像在async調用之前添加單詞"await"一樣簡單:

public byte[] ReadAHugeFile(string bigFile)
{
  var fileSize = new FileInfo(bigFile).Length; // Get the file size
  var allData = new byte[fileSize];      // Allocate a byte array as large as our file
  using (var fs = new System.IO.FileStream(bigFile, FileMode.Open))
  {
    await fs.ReadAsync(allData, 0, (int)fileSize); // Read the entire file asynchronously...
  }
  return allData;               // ...and return those bytes!
}

哦。如果你試過,你會發現有一個錯誤。這是因為.net需要知道這個方法是異步的,它最終會返回一個字節數組。因此,我們做的第一件事是在返回類型之前添加單詞“async”,然后用Task<…>,是這樣的:

public async Task<byte[]> ReadAHugeFile(string bigFile)
{
  var fileSize = new FileInfo(bigFile).Length; // Get the file size
  var allData = new byte[fileSize];      // Allocate a byte array as large as our file
  using (var fs = new System.IO.FileStream(bigFile, FileMode.Open))
  {
    await fs.ReadAsync(allData, 0, (int)fileSize); // Read the entire file asynchronously...
  }
  return allData;               // ...and return those bytes!
}

好吧!現在我們烹飪!如果我們現在運行我們的代碼,它將繼續在UI線程上運行,直到我們到達ReadAsync方法的await。此時,. net知道這是一個將由硬盤執行的活動,因此“await”將一個小書簽放在當前位置,然后UI線程返回到它的正常處理(所有的視覺更新等)。

隨后,一旦硬盤驅動器讀取了所有數據,ReadAsync方法將其全部復制到allData字節數組中,任務現在就完成了,因此系統按門鈴,讓原始線程知道結果已經準備好了。原始線程說:“太棒了!讓我回到離開的地方!”一有機會,它就會回到“await fs.ReadSync”,然后繼續下一步,返回allData數組,這個數組現在已經填充了我們的數據。

如果你在一個接一個地看一個例子,并且使用的是最近的Visual Studio版本,你會注意到這一行:

ReadAHugeFile(hugeFile);

…現在,它用綠色下劃線表示,如果將鼠標懸停在它上面,它會說,“因為這個調用沒有被等待,所以在調用完成之前,當前方法的執行將繼續。”考慮對調用的結果應用'await'操作符。"

這是Visual Studio讓你知道它承認ReadAHugeFile()是一個異步的方法,而不是返回一個結果,這也是返回任務,所以如果你想等待結果,然后你就可以添加一個“await”:

await ReadAHugeFile(hugeFile);

…但如果我們這樣做了,那么你還必須更新方法簽名:

public async void Loopy()

注意,如果我們在一個不返回任何東西的方法上(void返回類型),那么我們不需要將返回類型包裝在Task<…>中。

但是,我們不要這樣做。相反,讓我們來了解一下我們可以用異步做些什么。

如果你不想等待ReadAHugeFile(hugeFile)的結果,因為你可能不關心最終的結果,但你不喜歡綠色下劃線/警告,你可以使用一個特殊的技巧來告訴.net。只需將結果賦給_字符,就像這樣:

_ = ReadAHugeFile(hugeFile);

這就是.net的語法,表示“我不在乎結果,但我不希望用它的警告來打擾我。”

好吧,我們試試別的。如果我們在這一行上使用了await,那么它將等待第一個文件被異步讀取,然后等待第二個文件被異步讀取,最后等待第三個文件被異步讀取。但是…如果我們想要同時異步地讀取所有3個文件,然后在所有3個文件都完成之后,我們允許代碼繼續到下一行,該怎么辦?

有一個叫做Task.WhenAll()的方法,它本身是一個你可以await的異步方法。傳入其他任務對象的列表,然后等待它,一旦所有任務都完成,它就會完成。所以最簡單的方法就是創建一個List<Task>對象:

List<Task> readingTasks = new List<Task>();

…然后,當我們將每個ReadAHugeFile()調用中的Task添加到列表中時:

foreach (var hugeFile in hugeFiles) {  
   readingTasks.Add(ReadAHugeFile(hugeFile));
}

…最后我們 await Task.WhenAll():

await Task.WhenAll(readingTasks);

最終的方法是這樣的:

public async void Loopy()
{
  var hugeFiles = new string[] {
   "Gr8Gonzos_Home_Movie_In_8k_Res.mkv", // 1 GB
   "War_And_Peace_In_150_Languages.rtf", // 1.2 GB
   "Cats_On_Catnip.mpg"         // 0.9 GB
  };


  List<Task> readingTasks = new List<Task>();
  foreach (var hugeFile in hugeFiles)
  {
    readingTasks.Add(ReadAHugeFile(hugeFile));
  }
  await Task.WhenAll(readingTasks);


  MessageBox.Show(sb.ToString());
}

當涉及到并行活動時,一些I/O機制比其他機制工作得更好(例如,網絡請求通常比硬盤讀取工作得更好,但這取決于硬件),但原理是相同的。

現在,“await”操作符還要做的最后一件事是提取最終結果。所以在上面的例子中,ReadAHugeFile返回一個任務<byte[]>。await的神奇功能會在完成后自動拋出Task<>包裝器,并返回byte[]數組,所以如果你想訪問Loopy()中的字節,你可以這樣做:

byte[] data = await ReadAHugeFile(hugeFile);

再次強調,await是一個神奇的小命令,它使異步編程變得非常簡單,并為你處理各種各樣的小事情。

現在讓我們轉向多線程。

C#中的多線程

微軟有時會給你10種不同的方法來做同樣的事情,這就是它如何使用多線程。你有BackgroundWorker類、Thread和Task(它們有幾個變體)。最終,它們都做著相同的事情,只是有不同的功能。現在,大多數人都使用Task,因為它們的設置和使用都很簡單,而且如果你想這樣做的話(我們稍后會講到),它們也可以很好地與異步代碼交互。如果你好奇的話,關于這些具體區別有很多文章,但是我們在這里使用任務。

要讓任何方法在單獨的線程中運行,只需使用Task.Run()方法來執行它。例如,假設你有這樣一個方法:

public void DoRandomCalculations(int howMany)
{
  var rng = new Random();
  for (int i = 0; i < howMany; i++)
  {
    int a = rng.Next(1, 1000);
    int b = rng.Next(1, 1000);
    int sum = 0;
    sum = a + b;
  }
}

我們可以像這樣在當前線程中調用它:

DoRandomCalculations(1000000);

或者我們可以讓另一個線程來做這個工作:

Task.Run(() => DoRandomCalculations(1000000));

當然,有一些不同的版本,但這是總體思路。

Task. run()的一個優點是它返回一個我們可以等待的任務對象。因此,如果想在一個單獨的線程中運行一堆代碼,然后在進入下一步之前等待它完成,你可以使用await,就像你在前面一節看到的那樣:

var finalData = await Task.Run(() => {});

關于C#中異步與多線程的作用有哪些問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

蓬莱市| 德令哈市| 平定县| 都匀市| 桐梓县| 凉山| 濮阳市| 曲沃县| 青铜峡市| 麻栗坡县| 怀远县| 盐池县| 兰州市| 台北市| 宁远县| 桃园县| 曲周县| 于都县| 屏山县| 临泽县| 海伦市| 万宁市| 洞头县| 牙克石市| 比如县| 塔城市| 鹤岗市| 高碑店市| 大余县| 东光县| 沿河| 武穴市| 招远市| 靖州| 濉溪县| 印江| 武夷山市| 福建省| 理塘县| 乐亭县| 济源市|