异步、多线程、Await/Async、Task
异步多线程经常被拿来说事,网上文章也是多如牛毛,我也是停留在很菜的水平,痛下决心好好“恶补”一下这块知识。
还是先放两个官方文档压压惊:使用 Async 和 Await 的异步编程 .NET 中的并行处理、并发和异步编程
学习前:
耗时操作知道搞个Task.Run,不用等待结果就后台执行,Api直接返回,多个任务就循环启用Task。控制器方法加上async,调用加上await,别人这么用也跟着,不造为毛。Thread最常用的是Sleep。。。就这样。
学习后:
多线程的前世今生就不具体介绍了,简单粗暴的实操文章奉上:Thread、ThreadPool、Task、Parallel的基本用法、区别以及弊端
通过几个小例子Get不明白的知识点,搞一个winform,启动的同时再带一个控制台,方便输出对比,Program.cs需要加几行代码。大概放了这几个按钮,写了几个输出和调用异步的方法(完整代码在最后)。

1、同步
这就不说了,睡3秒顺序执行,有一点注意是文本框信息是没有即时更新的,winform正常情况下是主线程更新控件状态的,因为主线程被阻塞了,所以无法刷新UI,主线程释放后才执行。


2、异步中更新UI
Over的时候可以发现更新信息是由线程id为5的线程完成的,跨线程操作winform控件的方法交给委托,见红框。



3、异步结果更新UI
如果异步线程需要等待返回值可以这样写更简洁,方法上加上了async关键字,await等待线程执行,那么问题来了,异步不就又变成同步了?意义何在呢?这也是实操前一直不明白的点,这个例子看不出效果,接着操作。


4、调用异步
调用异步方法,自己本身也应该是异步的,如果没有async关键字,就会报出警告,并且没有async修饰的方法不能使用await关键字,这个例子只是把异步方法MethodAsync拿出去了,为了演示下面的例子使用。


5、异步调用死锁
这里调用异步方法MethodAsyncDeadlock,与MethodAsync的区别就是,把耗时函数的结果返回了,由于主线程没有等待异步调用返回,又输出了异步方法的结果,自然是拿不到返回值,因为异步方法还没有执行完成,所以这线程就“干架”了,卡这了,死锁。


6、解决死锁
死锁大部分是代码不规范造成的,代码写懵逼也是常有的事,解决也很简单,await出马,等异步方法完成返回再继续执行就OK了。


7、Thread、Parallel、Task
启动方式直接看代码,有一个小坑需要注意,就是多线程访问公共变量问题。

8、重点来了!!!
异步编程的意义何在这个哲学问题,换一个控制台例子更能说明问题。很简单,一个控制台、一个类。从执行结果可以看出来,Main方法开始,到MethodAsync异步方法执行,开启Task耗时函数之前都是线程1在干活儿,耗时函数由另外一个线程4执行,开始之前线程1已已经返回,并且输出了Main End,这意味着剩下的不管是耗时函数,还是等待耗时函数完成之后的逻辑,都不在需要主线程等待完成,由别的线程搞定。winform解释不来是因为异步完成之后还是交给了主线程UI去继续执行了,所以线程id总是前后一致。


9、结论!!!
实操分析完之后,就明白了之前忘记在哪看到的一句话了:异步能增加网站的吞吐量,但并不会让网站变得更快。
1、提高网站的响应能力,也是async/await也常见于Web项目的原因。
2、可以用同步的写法,完成异步操作,需要的时候启动线程并行执行,提高运算或逻辑处理能力,又能等待控制顺序,可以说贼爽。
参考了:C# 彻底搞懂async/await,也是言简意赅的文章。
附上代码(粗陋勿喷)
public partial class AsyncDome : Form
{
public AsyncDome()
{
InitializeComponent();
}
//同步
private void button1_Click(object sender, EventArgs e)
{
btnClickAop(sender, "Start", true);
Thread.Sleep(3000);
btnClickAop(sender, "End");
}
//异步中更新UI
private void button2_Click(object sender, EventArgs e)
{
btnClickAop(sender, "Start", true);
Task.Run(() =>
{
Thread.Sleep(3000);
showConsoleDelegate(bt(sender) + " Over");
});
btnClickAop(sender, "End");
}
//异步结果更新UI
private async void button3_Click(object sender, EventArgs e)
{
btnClickAop(sender, "Start", true);
var t = Task.Run(() =>
{
Thread.Sleep(3000);
return bt(sender) + " Over";
});
showConsole(await t);
btnClickAop(sender, "End");
}
//调用异步
private void button4_Click(object sender, EventArgs e)
{
btnClickAop(sender, "Start", true);
MethodAsync();
btnClickAop(sender, "End");
}
//异步调用死锁
private void button5_Click(object sender, EventArgs e)
{
btnClickAop(sender, "Start", true);
var ResultTask = MethodAsyncDeadlock();
showConsole(ResultTask.Result); //死锁原因:此行代码与调用方法抢占主线程,导致死锁。
btnClickAop(sender, "End");
}
//解决死锁
private async void button6_Click(object sender, EventArgs e)
{
btnClickAop(sender, "Start", true);
var ResultTask = MethodAsyncDeadlock();
showConsole(await ResultTask); //死锁解决:等待调用方法使用完成主线程即可。
btnClickAop(sender, "End");
}
//Thread循环
private void button7_Click(object sender, EventArgs e)
{
//Thread
for (int i = 0; i < 100; i++)
{
int newIndex = i;
new Thread(new ThreadStart(() => showConsoleDelegate("for: " + newIndex))).Start();
}
}
//Parallel循环
private void button8_Click(object sender, EventArgs e)
{
//Parallel
Parallel.ForEach<int>(new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, (str) =>
{
showConsoleDelegate(str.ToString());
});
}
//Task循环
private void button9_Click(object sender, EventArgs e)
{
//Task
for (int i = 0; i < 100; i++)
{
int newIndex = i;
Task.Run(() => { showConsoleDelegate("for: " + newIndex); });
}
}
#region 输出处理
private void btnClickAop(object sender, string flag, bool empty = false)
{
if (empty)
{
txtConsloe.Text = "";
Console.Clear();
}
showConsole(bt(sender) + " " + flag);
}
private void showConsole(string info)
{
info = getInfo(info);
txtConsloe.Text += info;
Console.WriteLine(info);
}
private void showConsoleDelegate(string info)
{
info = getInfo(info);
Console.WriteLine(info);
txtConsloe.Invoke(new Action(() => { txtConsloe.Text += info; }));
}
//输出信息
private string getInfo(string info)
{
return info + " Thread :" + Thread.CurrentThread.ManagedThreadId + " " + DateTime.Now.ToString("G") + Environment.NewLine;
}
//获取按钮文本
private string bt(object sender)
{
return ((Button)sender).Text;
}
#endregion
#region 异步方法
private async Task MethodAsync()
{
showConsole("MethodAsync Start");
string Result = await LongTimeMethod();
showConsole(Result);
showConsole("MethodAsync End");
}
private async Task<string> MethodAsyncDeadlock()
{
showConsole("MethodAsyncDeadlock Start");
string Result = await LongTimeMethod();
showConsole("MethodAsyncDeadlock End");
return Result;
}
private Task<string> LongTimeMethod()
{
var task = Task.Run(() =>
{
showConsoleDelegate("LongTimeMethod Start ");
Thread.Sleep(3000);
showConsoleDelegate("LongTimeMethod End ");
return "LongTimeMethod Over";
});
return task;
}
#endregion
}
异步、多线程、Await/Async、Task的更多相关文章
- Android菜鸟的成长笔记(13)——异步任务(Async Task)
原文:[置顶] Android菜鸟的成长笔记(13)——异步任务(Async Task) Android的UI线程主要负责处理用户的事件及图形显示,因此主线程UI不能阻塞,否则会弹出一个ANR(App ...
- 异步多线程 Thread ThreadPool Task
一.线程 Thread ThreadPool 线程是Windows任务调度的最小单位,线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针.程序计数器等),但代码区是共享的,即不同的线程可以 ...
- C# await async Task
//原文:https://www.cnblogs.com/yan7/p/8401681.html //原文:https://www.cnblogs.com/s5689412/p/10073507.ht ...
- Await Async Task
class Program { static void Main(string[] args) { Console.WriteLine("=======Start Main!======== ...
- C# 异步锁 await async锁,lock,Monitor,SemaphoreSlim
异步方法内无法使用Monitor 和lock 所以只能用System.Threading.SemaphoreSlim了 //Semaphore (int initialCount, int maxim ...
- 异步多线程 ASP.NET 同步调用异步 使用Result产生死锁
一个方法调用了async方法,要将这个方法本身设计为async. public class BlogController : Controller { public async Task<Act ...
- .Net 多线程 异步编程 Await、Async和Task
await和async简介 await和async是在C#5中引入,并且在.NetFramewor4.5以及.NetCore中进行了支持.主要是解决性能瓶颈,并且增强系统的响应能力. msdn关于 ...
- C#多线程和异步(二)——Task和async/await详解(转载)
一.什么是异步 同步和异步主要用于修饰方法.当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,我们称这个方法是同步方法:当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务 ...
- 异步方法的意义何在,Async和await以及Task的爱恨情仇,还有多线程那一家子。
前两天刚感受了下泛型接口的in和out,昨天就开始感受神奇的异步方法Async/await,当然顺路也看了眼多线程那几个.其实多线程异步相关的类单个用法和理解都不算困难,但是异步方法Async/awa ...
- C#.NET使用Task,await,async,异步执行控件耗时事件(event),不阻塞UI线程和不跨线程执行UI更新,以及其他方式比较
使用Task,await,async,异步执行事件(event),不阻塞UI线程和不跨线程执行UI更新 使用Task,await,async 的异步模式 去执行事件(event) 解决不阻塞UI线程和 ...
随机推荐
- 33家国内外知名的手机广告平台的SDK名单汇总,吐血推荐
下面格式:第一行是广告平台名称,第二行是SDK的包名特征,第三行是平台网站 多盟cn.domob.androidhttp://www.domob.cn/百度推广SDK com.baidu统计SDK c ...
- Java虚拟机(JVM):第四幕:自动内存管理 - 经典垃圾收集器
前言:如果说收集算法是内存回收的方法论,那么垃圾收集器则是内存回收的实践者.整哥Java堆 :Full GC. 1.Serial收集器:最基础.历史最悠久的收集器,这是一个单线程工作的收集器. 2.P ...
- Python如何在日志中隐藏明文密码
Python如何在日志中隐藏明文密码 前言 在项目开发中,有的时候会遇到一些安全需求,用以提升程序整体的安全性,提高外来非法攻击的门槛,而在日志中隐藏明文密码打印便是最典型的安全需求之一. 在Pyth ...
- [论文研读]空天地一体化(SAGIN)的网络安全_A_Survey_on_Space-Air-Ground-Sea_Integrated_Network_Security_in_6G
------------恢复内容开始------------ 空天地一体化(SAGIN)的网络安全 目前关注的方面: 集中在安全通信.入侵检测.侧通道攻击.GPS欺骗攻击.网络窃听.消息修改/注入等方 ...
- P4022 [CTSC2012]熟悉的文章 题解
题目链接 简要题意 给定 \(m\) 个模板串和 \(n\) 个匹配串,如果一个字符串是一个模板串的子串且长度不小于 \(L\) 则称其为"熟悉的",对于每个匹配串,求一个最大的 ...
- 数据结构-线性表-单链表(c++)
线性表的运算 求长度GetLength(L),求线性表L的长度 置空表SetNull(L),将线性表置成空表 按位查找Get(L,i),查找线性表L第i个元素 按值查找Location(L,x),查找 ...
- 持续集成(CI)、自动化构建和自动化测试--初探
转自:http://blog.csdn.net/adparking/article/details/5796532 此文章是为了总结前一段时间由于Maven2的学习而引起的一个持续集成的学习. 一.什 ...
- PC电脑端如何多开Skype,一步搞定!
由于工作原因,本人经常会用到Skype来联系客户,目前有两个账号需要同时登录. 但是,Skype默认只能登录一个账号,而且安装的时候也不能自定义安装地址,所以没办法同时登录两个. 有的朋友可能会想到直 ...
- GPTs破冰硅基文明社会
GPTs破冰硅基文明社会 渐进是技术革命的常态 技术革命看似一夕之间就颠覆了世界,但实际上每项重大技术进步的背后,都经历了漫长的渐进积累.以蒸汽机为例,最初动力微弱.效率低下,需要大量工程师跟车维护, ...
- 从一道题来看看golang中的slice作为参数时的现象
1.题目 最近看群友在群里问一道关于golang中slice的题,题目如下: package main import "fmt" func main() { k := []int{ ...