C# 中的同步、异步和多线程

18 篇文章 0 订阅
5 篇文章 0 订阅

尽管同步、异步和多线程之间的区别对大多数开发人员来说非常清楚,但在实际方面可能会产生一些疑问。

async-await 运算符向列表中添加了另外一种场景,在某些情况下可能会很棘手。

在开始讨论棘手的情况之前,我将简要解释一些基本概念。

过程

当一个计算机程序启动时,操作系统(OS)会创建一个进程来执行该程序,通常你可以在你的操作系统的应用程序管理器上看到进程ID,当关闭这个进程时你的应用程序就结束了。

线

线程进程的子集,用于执行应用程序的代码指令,通常一个进程以一个线程开始,但该进程可以创建和完成额外的线程

关闭进程时,所有相关线程都已完成。

阻塞线程

有些任务比其他任务需要更多的时间来执行,例如读取或写入大文件、访问网络资源或连接并针对数据库执行命令。

启动应用程序时,会创建一个新线程并开始执行所有代码指令,例如显示用户界面、呈现内容以及加载按钮和文本框等视觉组件。

想象一下,当点击一个按钮时,应用程序需要读取和处理一个大文件,在这种情况下,当线程忙于读取和处理文件时,用户界面将被阻塞,无法接受任何用户命令,如点击或滚动

同样的情况也可能发生在没有 API 等用户界面的应用程序上,当线程执行任务时,所有其他指令都必须等待。

多线程

解决这个问题的方法很简单,我们只需要创建一个额外的线程来执行长流程任务,然后主线程可以执行其他指令,比如让用户点击另一个按钮、滚动屏幕等。

当长进程完成执行时,附加线程关闭,应用程序从他等待的点继续执行。

异步

大多数长处理任务由操作系统而不是应用程序管理这一事实表明,创建一个新线程只是为了等待操作系统管理这些任务并不是很聪明。

这就是 async 发挥作用的地方。

异步方法将任务委托给操作系统,不会阻塞主线程,当操作系统完成处理时,它将作为回调返回调用点。

聪明多了,不是吗?

差异

下面我们可以通过图形看到同步、异步和多线程之间的执行差异。

同步呼叫。作者的图表

异步调用。作者的图表

等待操作符

有时需要一个长任务的结果来执行程序中的下一步,因此程序在操作系统执行任务时无法继续执行其他指令,在这种情况下,我们可以使用等待操作符,它将保持等待任务完成并返回执行下一条指令

重要提示:使用 await 不会阻塞主线程,这意味着在执行任务时应用程序不会无响应

棘手的情况。

使用 async-await 使开发人员的体验非常接近同步方法,但实际上并非如此。

不完全了解其工作原理的开发人员将始终使用 async-await 而不考虑实际发生的情况,并且当我们处理多个任务时,我们可以优化并行运行它们的应用程序。

为了更好地理解它,请参阅下面的问题

问题

我需要调用三个需要很长时间才能执行的方法,并且只有在所有任务完成后才能返回调用者。

我将演示两种方法并测量每个方法执行所花费的时间。

public class AwaitingTest
{
    public async Task AwaitSequencially() { ... }

    public async Task AwaitInParallel() { ... }

    private async Task wait5sec() => await Task.Delay(5000);
    private async Task wait5sec_2() => await Task.Delay(5000);
    private async Task wait5sec_3() => await Task.Delay(5000);
}

等待测试类

未优化的方法

在这里,我异步调用所有方法并按顺序等待它们。

public async Task AwaitSequencially()
{
    var watch = new Stopwatch();
    
    Console.WriteLine("Starting sequencially async calls");
    
    watch.Start();

    await wait5sec();
    await wait5sec_2();
    await wait5sec_3();

    watch.Stop();
    
    Console.WriteLine($"finished sequencially async calls in {watch.ElapsedMilliseconds}");
}

AwaitSequentially 方法

优化方法

现在,我首先创建所有任务,然后等待所有任务。

public async Task AwaitInParallel()
{
    var watch = new Stopwatch();
    
    Console.WriteLine("Starting parallel async calls");

    watch.Start();

    var a = wait5sec();
    var b = wait5sec_2();
    var c = wait5sec_3();

    Console.WriteLine("Doing other stuff");

    await a;
    await b;
    await c;
    
    watch.Stop();
    
    Console.WriteLine($"finished parallel async calls in {watch.ElapsedMilliseconds}");
}

AwaitInParallel 方法

结果

调用这两种方法会有很大不同的结果

截图

执行结果

第一种方法在 15 秒后完成,而第二种方法只用了 5 秒,因为它并行运行所有调用。

尽管 async-await 模型与同步模型非常相似,但它们并不相同,并且差异可能很大。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值