C#中的异步和多线程


	C#中的异步和多线程
[编程语言教程]

许多开发人员对异步代码和多线程以及它们的工作原理和使用方法都有错误的认识。在这里,你将了解这两个概念之间的区别,并使用c#实现它们。

我:“服务员,这是我第一次来这家餐厅。通常需要4个小时才能拿到食物吗?”

服务员:“哦,是的,先生。这家餐厅的厨房里只有一个厨师。”

我:“……只有一个厨师吗?”

服务员:“是的,先生,我们有好几个厨师,但每次只有一个在厨房工作。”

我:“所以其他10个穿着厨师服站在厨房里的人……什么都不做吗?厨房太小了吗?”

服务员:“哦,我们的厨房很大,先生。”

我:“那为什么他们不同时工作呢?”

服务员:“先生,这倒是个好主意,但我们还没想好怎么做。”

我:“好了,奇怪。但是…嘿…现在的主厨在哪里?我现在没看见有人在厨房里。”

服务员:“是的,先生。有一份订单的厨房用品已经用完了,所以厨师已经停止烹饪,站在外面等着送货了。”

我:“看起来他可以一边等一边做饭,也许送货员可以直接告诉他们什么时候到了?”

服务员:“又是一个绝妙的主意,先生。我们在后面有送货门铃,但厨师喜欢等。我去给你再拿点水来。”

多糟糕的餐厅,对吧?不幸的是,很多程序都是这样工作的。

有两种不同的方法可以让这家餐厅做得更好。

首先,很明显,每个单独的晚餐订单可以由不同的厨师来处理。每一种都是一个必须按特定顺序发生的事情列表(准备原料,然后混合它们,然后烹饪,等等)。因此,如果每个厨师都致力于处理这一清单上的东西,几份晚餐订单可以同时做出。

这是一个真实世界中的多线程示例。计算机有能力让多个不同的线程同时运行,每个线程负责按特定顺序执行一系列活动。

然后还有异步行为。需要明确的是,异步不是多线程的。还记得那个一直在等外卖的厨师吗?真是浪费时间!在等待的过程中,他没有做任何有意义的事情,比如做饭。而且,等待也不会让送货更快。一旦他打电话订购供应品,发货就会随时发生,所以为什么要等呢?相反,送货员只需按门铃,说一句:“嘿,这是你的供应品!”

有很多I/O活动是由代码之外的东西处理的。例如,向远程服务器发送一个网络请求。这就像给餐厅点餐一样。你的代码所做的唯一事情就是进行调用并接收结果。如果选择等待结果,在这两者之间完全不做任何事情,那么这就是“同步”行为。

然而,如果你更喜欢在结果返回时被打断/通知(就像送货员到达时按门铃),同时可以处理其他事情,那么这就是“异步”行为。

只要工作是由不受当前代码直接控制的对象完成的,就可以使用异步代码。例如,当你向硬盘驱动器写入一堆数据时,你的代码并没有执行实际的写入操作。它只是请求硬件执行该任务。因此,你可以使用异步编码开始编写,然后在编写完成时得到通知,同时继续处理其他事情。

异步的优点在于不需要额外的线程,因此非常高效。

“等等!”你说。“如果没有额外的线程,那么谁或什么在等待结果?代码如何知道返回的结果?”

还记得那个门铃吗?你的电脑里有一个系统叫做“中断”系统,它的工作原理有点像那个门铃。当你的代码开始一个异步活动时,它基本上会安装一个虚拟的门铃。当其他任务(写入硬盘驱动器,等待网络响应等)完成时,中断系统“中断”当前运行的代码并按下门铃,让你的应用程序知道有一个任务在等待!不需要线程坐在那里等待!

让我们快速回顾一下我们的两种工具:

多线程:使用一个额外的线程来执行一系列活动/任务。

异步:使用同一个线程和中断系统,让线程外的其他组件完成一些活动,并在活动结束时得到通知。

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!
}
hmoban主题是根据ripro二开的主题,极致后台体验,无插件,集成会员系统
自学咖网 » C#中的异步和多线程