当前位置: 移动技术网 > IT编程>开发语言>.net > C#中await/async闲说

C#中await/async闲说

2019年06月25日  | 移动技术网IT编程  | 我要评论

衢州一中教师招聘,飞雪玉花变奏曲,日成人网

自从c#5.0增加异步编程之后,异步编程越来越简单,async和await用的地方越来越多,越来越好用,只要用异步的地方都是一连串的异步,如果想要异步编程的时候,需要从底层开始编写,这样后边使用的时候就是异步,那么底层是如何实现??我们如何编写高效率的异步方法??

#了解基于任务的异步模式(tap)

基于任务的异步编程模型 (tap) 提供了异步代码的抽象化,你只需像往常一样将代码编写为一连串语句即可,在开始调用的地方运行。例如:var task = method()①; await task②; 在①的时候开始运行可能还没有运行完,在②程序挂起等待运行完,中间怎么运行的你不需要知道,编译器会做若干操作的。当开启多个任务的时候,像要他们都执行完,在执行其他的时候,可以await task.whenall(task1,task2 .....);

#了解async/await

await 运算符应用于异步方法,在方法的执行中插入挂起点,直到所等待任务完成。使用async 和await定义异步方法不一定会创建新线程,当编译器看到await关键字时,线程会挂起等待运行结束。
await 仅可用于由 async 关键字修改的异步方法中,使用 async 修饰符定义的方法通常包含一个或多个 await 表达式,使用await运算符的任务通常是实现[基于任务的异步模式(tap)]的方法调用返回,返回值包括 task、task<tresult>、valuetask 和 valuetask<tresult> 对象的方法。

# 调用 task.wait() 或者 task.result 立刻产生死锁的充分条件

1. 调用 wait() 或 result 的代码位于 ui 线程。
2. task 的实际执行在其他线程,且需要返回 ui 线程。
死锁的原因:uwp、wpf、windows forms 程序的 ui 线程都是单线程的。为了避免产生死锁,你应该一条道走到黑, async all the way。或者.configureawait(false)

# valuetask与task的区别

7.0为async新增的valuetask的作用(如果没有在nuget上下载system.threading.tasks.extensions,valuetask就在这个库中),valuetask用于值类型的异步;task为引用类型的,每次需要分配空间。
例如:

public async task<int> calculatesum(int a, int b) {
    if (a == 0 && b == 0)
    {
        return 0;
    }

    return await task.run(() => a + b);
}

当a,b=0的时候不会运行到task里,这个时候返回task就造成了资源的浪费,修改为以下会效率更高

public async valuetask<int> calculatesum2(int a, int b)
{
    if (a == 0 && b == 0)
    {
        return 0;
    }

    return await task.run(() => a + b);
}

但是也不是说到处用valuetask会好,当是引用类型的时候,用valuetask,你需要关注更多的数据,这个时候用task会更好。

# await/async原理分析

[asyncstatemachine(typeof(class1.<calculatesum2>d__1))]
public valuetask<int> calculatesum2(int a, int b)
{
    class1.<calculatesum2>d__1 <calculatesum2>d__;
    <calculatesum2>d__.a = a;
    <calculatesum2>d__.b = b;
    <calculatesum2>d__.<>t__builder = asyncvaluetaskmethodbuilder<int>.create();
    <calculatesum2>d__.<>1__state = -1;
    asyncvaluetaskmethodbuilder<int> <>t__builder = <calculatesum2>d__.<>t__builder;
    <>t__builder.start<class1.<calculatesum2>d__1>(ref <calculatesum2>d__);
    return <calculatesum2>d__.<>t__builder.task;
}

对calculatesum2代码解析,发现没有await/async,原来又是编译器提供的语法糖。

[__dynamicallyinvokable, debuggerstepthrough, securitysafecritical]
public void start<tstatemachine>(ref tstatemachine statemachine) where tstatemachine : iasyncstatemachine
{
    if (statemachine == null)
    {
        throw new argumentnullexception("statemachine");
    }
    executioncontextswitcher executioncontextswitcher = default(executioncontextswitcher);
    runtimehelpers.prepareconstrainedregions();
    try
    {
        executioncontext.establishcopyonwritescope(ref executioncontextswitcher);
        statemachine.movenext();
    }
    finally
    {
        executioncontextswitcher.undo();
    }
}

对start方法进行分析,可以看出movenext,程序的运行其实还是一步一步进行的,那么await/async会不会创建一个线程,这倒是不一定,这个由线程池决定,那么异步了不创建一个线程,怎么异步的,这里的异步可能是运行在已经有的线程上。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网