目录
网上已经有很多大牛对异步相关进行了深入的探讨,在此整合一下加入自己的理解学习下。
1、进程与线程
1.1 堆栈
内存格局通常分为四个区:
全局数据区:存放全局变量,静态数据,常量
代码区:存放所有的程序代码
栈区:存放为运行而分配的局部变量,参数,返回数据,返回地址等,
堆区:即自由存储区
线程堆栈(Thread Stack)和托管堆(Managed Heap)。
每个正在运行的程序都对应着一个进程(process),在一个进程内部,可以有一个或多个线程(thread),每个线程都拥有一块“自留地”,称为“线程堆栈”,大小为1M,用于保存自身的一些数据,比如函数中定义的局部变量、函数调用时传送的参数值等,这部分内存区域的分配与回收不需要程序员干涉。
堆栈中存放的是什么?
1.引用类型总是被分配到“堆”上。
2.值类型总是分配到它声明的地方:
a.作为引用类型的成员变量分配到“堆”上
b.作为方法的局部变量时分配到“栈”上
1.2 CPU时间片
时间片即CPU分配给各个程序的时间,每个线程被分配一个时间段,称作它的时间片,即该进程允许运行的时间,使各个程序从表面上看是同时进行的。如果在时间片结束时进程还在运行,则CPU将被剥夺并分配给另一个进程。如果进程在时间片结束前阻塞或结束,则CPU当即进行切换。而不会造成CPU资源浪费。在宏观上:我们可以同时打开多个应用程序,每个程序并行不悖,同时运行。但在微观上:由于只有一个CPU,一次只能处理程序要求的一部分,如何处理公平,一种方法就是引入时间片,每个程序轮流执行。
进程运行的CPU时间(CPU)=执行代码的CPU时间(USER)+系统调度所用的CPU时间(Sys)
1.3 进程与线程
进程是操作系统分配和使用系统资源的基本单位。
进程资源包括:
- 一个进程堆;
- 一个或多个线程;
- 一个虚拟地址空间,该空间独立于其他进程的地址空间;
- 一个或多个代码段,包括.dll中的代码;
- 一个或多个包含全局变量的数据段;
- 环境字符串,包含环境变量信息;
- 其他资源,比如打开的句柄、其他的堆等;
线程是处理器上系统独立调度和时间分配的最基本的执行单元。是轻量级进程,是进程的一个实体(线程本质上是进程中一段并发运行的代码)
线程间的数据交换有两种方式:
(1)共享内存方式shared memory(共享堆):最大的优势是快速
(2)消息传递方式message passing(不需要共享堆):优势在于安全
个人理解:
进程只是资源的一个边界,代码的执行靠CPU执行。而CPU的分配靠操作系统调度。所以要想把进程中的代码尽快执行完得需要操作系统高频率的分配CPU给该进程中的线程。
2、前台与后台线程
当一个程序启动时,就有一个进程被操作系统(OS)创建,与此同时一个线程也立刻运行,该线程通常叫做程序的主线程(Main Thread),因为它是程序开始时就执行的,如果你需要再创建线程,那么创建的线程就是这个主线程的子线程,它是前台线程。
新建的子线程可以是前台线程或者后台线程,前台线程必须全部执行完,即使主线程关闭掉,这时进程仍然存活。后台线程在未执行完成时,如果前台线程关掉,则后台线程也会停掉,且不抛出异常。也就是说,前台线程与后台线程唯一的区别是后台线程不会阻止进程终止。可以在任何时候将前台线程修改为后台线程。
3、同步与异步
个人理解是同步指在调用函数时,会等待被调用的函数执行完才会继续执行后面的代码。
异步就是在调用函数时,不用等被调用的函数执行完即可继续乡下执行。
如下:同步情况下函数3要等函数2执行完才能执行;如果函数2是异步执行的,函数3就可以和函数2并发执行了。但是异步不一定比同步执行的时间快,因为线程的调度和上下文切换也是很费时间的。
代码示例:(字符串拼接挺耗时的)
/// <summary>
/// 异步执行
/// </summary>
public void DoMainAsync()
{
DoSub1();
var task= Task.Run(() => { DoSub1(); }); // 此处异步执行
DoSub1();
DoSub1();
task.Wait();
}
/// <summary>
/// 同步执行
/// </summary>
public void DoMainSync()
{
DoSub1();
DoSub1(); // 此处同步执行
DoSub1();
DoSub1();
}
public void DoSub1()
{
string sResult = string.Empty;
int iCount = 0;
for (int i = 0; i < 50000; i++)
{
iCount += i;
sResult += iCount.ToString();
}
}
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
//MainOperator.Instance.Domain();
MainOperator.Instance.DoMainSync();
stopWatch.Stop();
Console.WriteLine(string.Format("同步共耗时:{0}", stopWatch.ElapsedMilliseconds));
stopWatch.Start();
MainOperator.Instance.DoMainAsync();
stopWatch.Stop();
Console.WriteLine(string.Format("异步共耗时:{0}", stopWatch.ElapsedMilliseconds));
Console.Read();
耗时:
执行了很多次,同步一般在20000毫秒,异步一般41000毫秒。异步执行不一定比同步快,如果没用利用多核的特性,反而增加了系统调度的性能开销使程序执行的效率降低了。
示例二:(将第二步改造一下,执行数据库查询操作并读取出来,WHERE 条件不一样是防止数据库查询时走相同的执行计划)
/// <summary>
/// 异步执行
/// </summary>
public void DoMainAsync()
{
DoSub1();
//var task= Task.Run(() => { DoSub1(); });
var task = Task.Run(() => { DoSub2("SELECT fldm,ssqy FROM TEST WHERE 3=3"); }); // 此处异步执行
DoSub1();
DoSub1();
task.Wait();
}
/// <summary>
/// 同步执行
/// </summary>
public void DoMainSync()
{
DoSub1();
//DoSub1();
DoSub2("SELECT fldm,ssqy FROM TEST WHERE 2=2"); // 此处同步执行
DoSub1();
DoSub1();
}
public void DoSub1()
{
string sResult = string.Empty;
int iCount = 0;
for (int i = 0; i < 50000; i++)
{
iCount += i;
sResult += iCount.ToString();
}
}
public void DoSub2(string sSQL)
{
string sConnStr = string.Format(m_sOracleConnectionstring, "ORCL_XXXX", "test", "test");
OracleConnection oracleConn = new OracleConnection(sConnStr);
oracleConn.Open();
OracleCommand command = oracleConn.CreateCommand();
command.CommandType = System.Data.CommandType.Text;
command.CommandText = sSQL;
var reader = command.ExecuteReader();
while (reader.Read())
{
var content = reader.GetValue(0);
}
}
耗时:
异步操作的时间减少了,而同步操作的时间反而增加了。说明让等待数据库查询的操作让其它线程去做,等待数据库并不消耗太多CPU执行,让CPU更多的去执行主线程的任务,提高了程序执行的效率。
4、异步使用时机
如果需要 I/O 绑定(例如从网络请求数据或访问数据库),则需要利用异步编程。 还可以使用 CPU 绑定代码(例如执行成本高昂的计算),对编写异步代码而言,这是一个不错的方案。
红框中的描述对上文中异步比同步执行还要耗时作出了解释。
5、参考地址:
堆栈:https://www.cnblogs.com/shenfengok/archive/2011/09/06/2169306.html
堆栈:https://blog.csdn.net/leewhoee/article/details/16933173
时间片:https://blog.csdn.net/qq_32812243/article/details/50590104
线程调度:https://www.cnblogs.com/x-xk/archive/2012/12/03/2795702.html
值和引用类型的存储:https://blog.csdn.net/itx2000/article/details/17240081
进程与线程:https://www.cnblogs.com/wjcx-sqh/p/6045013.html
前台后台线程:https://www.cnblogs.com/gudi/p/6233977.html
Microsoft .Net异步编程文档:https://docs.microsoft.com/zh-cn/dotnet/csharp/async