当前位置: 首页 > news >正文

深圳网站制作教程浙江网站建设平台

深圳网站制作教程,浙江网站建设平台,做网站开发要具备什么知识,东莞网站没计前言 在文章《async和await》中,我们观察到了一下客观的规律,但是没有讲到本质,而且还遗留了一个问题: 这篇文章中,我们继续看看这个问题如何解决! 我们再看看之前写的代码: static public void TestWait2() {var t…

前言

在文章《async和await》中,我们观察到了一下客观的规律,但是没有讲到本质,而且还遗留了一个问题:

这篇文章中,我们继续看看这个问题如何解决!

问题遗留

我们再看看之前写的代码:

static public void TestWait2()
{var t = Task.Factory.StartNew(async () =>{Console.WriteLine("Start");await Task.Delay(3000);Console.WriteLine("Done");});t.Wait();Console.WriteLine("All done");}static public void TestWait3()
{var t = Task.Run(async () =>{Console.WriteLine("Start");await Task.Delay(3000);Console.WriteLine("Done");});t.Wait();Console.WriteLine("All done");
}

当时问题是,为啥 Task.Factory.StartNew 可以看到异步效果,而Task.Run中却是同步效果。
那其实是因为,Task.Factory.StartNew 返回的 t.Wait(); 它没卡住主线程,而Task.Run的 t.Wait();它卡住了。

那为啥,Task.Factory.StartNew没卡住呢?
这是应为 Task.Factory.StartNew 返回的变量 t 他是Task< Task >类型!

如果,Task.Run 返回的是Task类型,如果我们改成Task.Factory.StartNew,那么它 返回的类型就是Task<Task< int >>

在.Net4.0中提供一个Unwrap方法,用于将Task<Task< int>>解为Task< int>类型,所以如果代码改为:

static public async void Factory嵌套死等()
{Console.WriteLine($"Factory不嵌套死等{getID()}");var t = Task.Factory.StartNew(async() =>{Console.WriteLine($"Start{getID()}");await Task.Delay (1000);Console.WriteLine($"Done{getID()}");}).Unwrap();t.Wait();Console.WriteLine($"All done{getID()}");
}

那么此时 t.Wait(); 也能卡死主线程。

其实Task.Run(.net4.5引入) 是在 Task.Factory.StartNew(.net4.0引入) 之后出现的,Task.Run是为了简化Task.Factory.StartNew的使用。

t.Wait() 和 await t;

现在我从另一个角度分析问题。
使用 Task.Run,能不能达到异步的效果? 答案是肯定的!
不过,我们此时不应该使用 t.Wait(); 而是应该是 await t;

static public async void Run嵌套Await()
{Console.WriteLine($"Run嵌套Await{getID()}");var t = Task.Run(async () =>{Console.WriteLine($"Start{getID()}");await Task.Delay(1000);Console.WriteLine($"Done{getID()}");});await t;Console.WriteLine($"All done{getID()}");
}

在这里插入图片描述

这样的话就实现了异步效果。

await 是如何实现异步的

这里我们可以进一步分析一下。
“1” 是主线程的ID “5” 是 task 启的子线程 ID。
我发现All done 在 Done 后面执行的,这是应为 await t; 把主线程"遣返了"
而await t; 之后的代码(也就是All done 这句话的打印)是由子线程5接着完成的。

整个流程是这样的,当编译时,编译器看到了函数使用了 async 关键字,那么整个函数将被转换为一个带有状态机的函数,反编译后发现函数名称变为MoveNext。

当主线程执行到子函数时,遇到 await 那么此时 主线程就会返回(跳出整个子函数,去执行下一个函数),MoveNext呢就会切换状态机(由于状态机已经切换,下次MoveNext在被调用时,就会从await 处向下执行)。
不过,从现象看await 之后的代码,不是主线程调用了,而是Task的子线程。子线程会再次调用MoveNext,并且进入一个新的状态机。
这里就有一个结论,当主线程进入一个子函数,遇到await机会从函数直接返回,函数中以下的代码交给新的子线程执行。
为了证明一这一点,我又写了一个程序:

static public async Task AsyncInvoke()
{await Task.Run(() =>{Console.WriteLine($"This is 1 ManagedThreadId={Thread.CurrentThread.ManagedThreadId.ToString("00")}");Thread.Sleep(1000);});Console.WriteLine($"1{getID()}");await Task.Run(() =>{Console.WriteLine($"This is 2 ManagedThreadId={Thread.CurrentThread.ManagedThreadId.ToString("00")}");Thread.Sleep(1000);});Console.WriteLine($"2{getID()}");await Task.Run(() =>{Console.WriteLine($"This is 3 ManagedThreadId={Thread.CurrentThread.ManagedThreadId.ToString("00")}");Thread.Sleep(1000);});Console.WriteLine($"3{getID()}");await Task.Run(() =>{Console.WriteLine($"This is 4 ManagedThreadId={Thread.CurrentThread.ManagedThreadId.ToString("00")}");Thread.Sleep(1000);});Console.WriteLine($"4{getID()}");
}

执行效果如下:
在这里插入图片描述
你会发现不过一个函数里面有多少个await ,主线程遇到一个await就返回了,就跳出这个函数去执行其他的函数了。
函数剩下的await 后面的都是由子线程完成的!多个await 只是多个几个状态机而已。
所以在一个函数中,如果有个多个await ,除了第一个后面的都和主线程无关。

这里又出现了一个新的问题,为啥后面的线程ID都是5?这个其实不一定的,我重新跑了一次:
在这里插入图片描述
这次发现出现了两个子线程号 3 和 5,这是应为 Task 背后有个 线程池。Task 被翻译为任务,单纯的线程是指的Thread
Task 启动后,使用哪个线程是由背后的线程池提供,而这个线程池是由.net进行维护。包括回调什么时候发生都是由线程池中的一个线程通知Task对象!await 操作符 其实是 调用 Task对象的 ContinueWith,所以上面这段代码也可以这么写:

/// <summary>
/// 回调式写法
/// </summary>
public void TaskInvokeContinue()
{Task.Run(() =>{Console.WriteLine($"This is 1 ManagedThreadId={Thread.CurrentThread.ManagedThreadId.ToString("00")}");Thread.Sleep(1000);}).ContinueWith(t =>{Console.WriteLine($"This is 2 ManagedThreadId={Thread.CurrentThread.ManagedThreadId.ToString("00")}");Thread.Sleep(1000);}).ContinueWith(t =>{Console.WriteLine($"This is 3 ManagedThreadId={Thread.CurrentThread.ManagedThreadId.ToString("00")}");Thread.Sleep(1000);}).ContinueWith(t =>{Console.WriteLine($"This is 4 ManagedThreadId={Thread.CurrentThread.ManagedThreadId.ToString("00")}");Thread.Sleep(1000);});//不太爽---nodejs---回调式写法,嵌套是要疯掉
}

这就进一步体现了 await 用同步的方式,写异步的代码。
能实现这个的原因就是,函数已经被改造成一个状态机了。

到这里,我就把上次坑给填上了!下次我们在一起掰扯掰扯Task的一些有意思的用法。

小结

我觉得最重要的一点就是:
主线程遇到一个await就返回了,如何理解这个返回?
返回就是跳出这个函数,和这个函数没有半毛钱关系了,去执行其他下面的函数了。
该函数剩下的await后面的部分 都是由线程池中的子线程完成的!
理解这一点,有助于我们对异步代码的编写!

2023年7月29日 更新 (一次Debug的分享)

昨天才写完这篇文章,今天就发现之前的写的一段代码有问题。没想到这么快就用上了~~(笑哭)

程序大概是这样的。我有一个主线程,里面有两个函数A和B,A和B实现了 async await 。
A和B里有一句 await tcpcli.SendAsync(str) 这句异步代码, 大致代码如下:

while(true)
{await  A(){....bool b = await tcpcli.SendAsync(str);}await  B(){....await tcpcli.SendAsync(str);}
}

正常情况下,这样没啥问题。程序都是正常跑。但是当Tcp服务那边反应延时的时候,就会出问题。
运行到 bool b = await tcpcli.SendAsync(str); 时,按照之前的结论,主线程都是直接返回的,就会直接执行B
然后再接着执行A,但是如果bool b = await tcpcli.SendAsync(str); 依然还在等待,之线程还是会返回的,
此时会再次开一个新的线程,导致多个线程并发,但是我这里其他逻辑并发的话是会有问题的(比如写Modbus的一个寄存器)。
所以,一旦 tcpcli.SendAsync(str)卡住了,逻辑就出问题了!

既然逻辑不能并发,我当时为啥不直接用同步的方式呢?其实原因是当时我不知道如何用同步的方式获取返回值。
我当时 调用 tcpcli.SendAsync(str).Wait(); 时发现这个Wait()返回值是空,但是我又需要返回值,所以就用了
bool b = await tcpcli.SendAsync(str); 那其实如果想用同步的方式获取返回值,应该使用:
bool b = tcpcli.SendAsync(str).GetAwaiter().GetResult();
所以,最后改程序为:

while(true)
{await  A(){....bool b = tcpcli.SendAsync(str).GetAwaiter().GetResult();}await  B(){....tcpcli.SendAsync(str).Wait();}
}

http://www.hkea.cn/news/471338/

相关文章:

  • 河南省网站建设哪家好免费观看行情软件网站进入
  • 粘合剂东莞网站建设体育热点新闻
  • 百度网站排名关键词整站优化培训网站建设
  • 网络平台代理seo外包 杭州
  • 东方头条网站源码免费推广软件工具
  • 北京网站建设公司分享网站改版注意事项流程优化四个方法
  • 案例学 网页设计与网站建设手机百度seo快速排名
  • 江门网站建设总部电话产品推广渠道有哪些
  • 网站建设全攻略站长之家ping检测
  • 导航网站 cmsgoogle chrome谷歌浏览器
  • wordpress看其他人博客优化师是做什么的
  • 现在哪个网站还做白拿2021小说排行榜百度风云榜
  • 网站流量seo提升seo排名的方法
  • 做html网站模板下载地址网站页面布局和样式设计
  • 公司网站邮箱费用磁力宅在线搜种子
  • wordpress 缺少临时文件夹刷关键词优化排名
  • 做网站要有什么团队淘宝关键词排名查询工具
  • 开源门户网站源码宁波谷歌seo
  • wordpress+一页一屏seo关键技术有哪些
  • 学校校园网站建设实施方案精准营销的案例
  • 腾讯云服务器可以做网站可以推广发广告的app
  • seo外链友情链接网站运营推广选择乐云seo
  • 做网站 要学 什么语言网站优化公司
  • 天乐测绘网做网站吗搜索引擎广告图片
  • 湖南营销型网站建设多少钱百度关键词优化软件网站
  • 怎样给网站做关键词优化百度词条
  • 做网站哪个平台搭建网站需要什么技术
  • 做gif图的网站简述网络营销的主要方法
  • 做图网站被告seo视频网页入口网站推广
  • 做的网站底部应该标注什么意思免费文案素材网站