20181103_C#线程初探, BeginInvoke_EndInvoke
在C#中学习多线程之前, 必须要深刻的理解委托; 基本上所有的多线程都在靠委托来完成
一. 进程和线程:
a) 进程和线程都是计算机的概念, 跟程序语言没有任何关系
b) 进程和线程都属于计算机操作系统自身控制和调度, 程序语言只有使用的份, 最终的控制权还是得操作系统说了算, 程序语言最多有提醒功能, 比如叫线程休眠/挂起/终止, 至于操作系统听不听, 做不做, 什么时候做, 那是操作系统高兴不高兴的事.
c) 在任务管理器中, 下图每一个都是一个进程
d) 在任务管理器中, 性能中可以看到线程:
e)进程: 一个程序运行时, 占用计算机所有资源的总和; (CPU/内存/磁盘/GPU/IO)
f) 线程: 程序执行流的最小单位, 任何操作的执行都是由线程完成的, 线程是依托于进程存在的, 一个进程可以包含多个线程, 线程也可以有自己的计算资源
g)多线程: 多个执行流同时执行
二. 同步异步多线程:
a)同步和异步都是对方法的描述
i. 同步:
- 在一个方法体内一步一步的按照代码编写的顺序(分支/循环)来依次执行;
- 同步方法卡界面,主(UI)线程忙于计算
- 同步方法慢,只有一个线程干活
- action.Invoke();→属于同步调用
ii.异步:
- 不会等待方法的完成,会直接进入下一行 非阻塞;
- 异步多线程方法不卡界面,主线程完事儿了,计算任务交给子线程在做;
- 异步多线程方法快,因为多个线程并发运算
- 应用: winform提升用户体验;web一个业务操作后要发邮件,异步发送邮件 ; action.BeginInvoke()→属于异步调用
iii. 多线程, 启动无序, 执行时间不确定, 结束无序
三. 同步异步和多线程:
a) 同步异步是方法执行的概念
b) 线程/进程属于计算机概念
四. 示例和代码:
所有演示都用到的DoSomething方法代码如下:
/// <summary>
/// 一个比较耗时耗资源的私有方法
/// </summary>
/// <param name="name"></param>
private void DoSomethingLong(string name)
{
Console.WriteLine($"****************DoSomethingLong {name} Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
long lResult = 0;
for (int i = 0; i < 1000000000; i++)
{
lResult += i;
} Console.WriteLine($"****************DoSomethingLong {name} End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {lResult}***************");
}
a) BeginInvoke属于异步调用, 当任务完成之后, 使用当前的线程调用CallBack指定的动作; 再说一遍: BeginInvoke是当 当前线程 完成 后, 再用当前的线程来执行callback的任务; 下面代码演示
Action<string> action = this.DoSomethingLong; IAsyncResult asyncResult = null;
//异步回调
AsyncCallback callback = ia =>
{
//Console.WriteLine(object.ReferenceEquals(asyncResult, ia)); //true
//Console.WriteLine(ia.AsyncState); //ia.AsyncState 这个值就是传递过来的hao; 可以看做是参数
//Console.WriteLine($"到这里计算已经完成了。{Thread.CurrentThread.ManagedThreadId.ToString("00")}。");
};
//对于BeginInvoke的理解: 相当于.net框架为我们做了一个小封装, 当使用BeginInvoke的时候, .net框架从线程池, 拿一个线程出来执行beginInvoke, 当beginInvoke执行完成之后, //产生一个变量(asyncResult), 作为一个结果来描述这个线程, 然后这个变量又会作为一个参数来调用callback并执行, 所以当你使用object.ReferenceEquals(asyncResult, ia)发现结果是个true
//后面这个hao, 表示状态参数, 也就是说如果要在回调的时候使用某些信息, 则可以通过这个参数进行传递 //"btnAsyncAdvanced_Click" 是传递给调用者的参数; 比如 Action<string> a = DoRunData; a.BeginInvoke("abc", null, a);那么 // DorunData(string s); 这个函数在被调用时, 参数的值就是 abc
asyncResult = action.BeginInvoke("btnAsyncAdvanced_Click", callback, "hao"); Console.WriteLine($"到这里计算已经完成了。{Thread.CurrentThread.ManagedThreadId.ToString("00")}。");
b) 启动多线程去并行计算, 但是主线程又得真的等待到主线程把事情做完之后, 才能返回计算结果, 进行后续的执行, 下面代码演示使用asyncResult.IsCompleted完成等待:
IAsyncResult asyncResult = null;
//异步回调
AsyncCallback callback = ia =>
{
Console.WriteLine($"到这里计算已经完成了。{Thread.CurrentThread.ManagedThreadId.ToString("00")}。");
};
asyncResult = action.BeginInvoke("btnAsyncAdvanced_Click", callback, "hao"); Console.WriteLine($"到这里计算已经完成了。{Thread.CurrentThread.ManagedThreadId.ToString("00")}。"); int i = 0; //IsCompleted判断异步操作是否完成
while (!asyncResult.IsCompleted)//1 卡界面:主线程忙于等待
{ //可以等待,边等待边做其他操作 ; 做一点用户提示
//可能最多200ms的延迟
if (i < 10)
{
Console.WriteLine($"文件上传完成{i++ * 10}%..");//File.ReadSize
}
else
{
Console.WriteLine($"文件上传完成99.9%..");
}
Thread.Sleep(200);
}
Console.WriteLine($"上传成功了。。。..");
c)下面的代码演示使用asyncResult.AsyncWaitHandle.WaitOne(1000);进行等待
Action<string> action = this.DoSomethingLong;
IAsyncResult asyncResult = null;
//异步回调
AsyncCallback callback = ia =>
{
Console.WriteLine("我在callback里. . .");
};
asyncResult = action.BeginInvoke("btnAsyncAdvanced_Click", callback, "hao"); Thread.Sleep(300);
Console.WriteLine("执行其他的代码. . .");
Console.WriteLine("执行其他的代码. . .");
Console.WriteLine("执行其他的代码. . ."); asyncResult.AsyncWaitHandle.WaitOne();//等待任务的完成 ; 上面的方式查看状态的等待事件是否完成, 这种方式是通过信号量来实现的
asyncResult.AsyncWaitHandle.WaitOne(-1);//等待任务的完成; -1这个参数和不传递参数是一样的, 不传递参数也表示一直等待, 传递-1也表示一直等待
asyncResult.AsyncWaitHandle.WaitOne(1000);//限时等待;但是最多等待1000ms; 比如调用远程接口, 超过什么时间就不再等待
d)下面的代码演示使用EndInvoke来等待, 并获取返回值; 在用BeginInvoke异步调用方法时, EndInvoke方法会一直阻塞,等待被调用的方法执行完毕.
Func<int> func = () =>
{
Thread.Sleep(2000);
return DateTime.Now.Day;
};
Console.WriteLine($"func.Invoke()={func.Invoke()}"); IAsyncResult asyncResult1 = func.BeginInvoke(r =>
{
func.EndInvoke(r); //通常来讲, EndInvoke会写在BeginInvoke的里面, 表示只调用一次; 另外注意: EndInvoke只能调用一次. 这一行和下面一行只能存在一个
Console.WriteLine(r.AsyncState);
}, "孙悟空");
//Console.WriteLine($"func.EndInvoke(asyncResult)={func.EndInvoke(asyncResult1)}");
e) 三种线程等待方式的总结:
//1. asyncResult.IsCompleted 可以边等待, 边做其他的事情
//2. asyncResult.AsyncWaitHandle.WaitOne(1000); 可以做限时等待
//3. action.EndInvoke(asyncResult)可以做返回值等待
20181103_C#线程初探, BeginInvoke_EndInvoke的更多相关文章
- WebWorker:工作者线程初探
WebWorker:工作者线程初探 参考资料: 1.Web Worker 使用教程 - 阮一峰:http://www.ruanyifeng.com/blog/2018/07/web-worker.ht ...
- tomcat线程初探
博主:handsomecui,希望路过的各位大佬留下你们宝贵的意见,在这里祝大家冬至快乐. 缘由: 初探缘由,在业务层想要通过(当前线程的栈)来获取到控制层的类名,然后打日志,可是发现并不能通过当前线 ...
- 2016/1/2 Python中的多线程(1):线程初探
---恢复内容开始--- 新年第一篇,继续Python. 先来简单介绍线程和进程. 计算机刚开始发展的时候,程序都是从头到尾独占式地使用所有的内存和硬件资源,每个计算机只能同时跑一个程序.后来引进了一 ...
- LINUX线程初探
LINUX程序设计最重要的当然是进程与线程.本文主要以uart程序结合键盘输入控制uart的传输. 硬件平台:树莓派B+ 软件平台:raspberry 须要工具:USB转TTL(PL2303)+ ...
- Thread线程初探
using System; using System.Threading; class Example { static void Main() { TimeSpan interval = , , ) ...
- [转载]Linux 线程实现机制分析
本文转自http://www.ibm.com/developerworks/cn/linux/kernel/l-thread/ 支持原创.尊重原创,分享知识! 自从多线程编程的概念出现在 Linux ...
- Linux线程学习(二)
线程基础 进程 系统中程序执行和资源分配的基本单位 每个进程有自己的数据段.代码段和堆栈段 在进行切换时需要有比较复杂的上下文切换 线程 减少处理机的空转时间,支持多处理器以及减少上下文切换开销, ...
- 线程模型、pthread 系列函数 和 简单多线程服务器端程序
一.线程有3种模型,分别是N:1用户线程模型,1:1核心线程模型和N:M混合线程模型,posix thread属于1:1模型. (一).N:1用户线程模型 “线程实现”建立在“进程控制”机制之上,由用 ...
- Linux 线程实现机制分析--转
http://www.ibm.com/developerworks/cn/linux/kernel/l-thread/ 一.基础知识:线程和进程 按照教科书上的定义,进程是资源管理的最小单位,线程是程 ...
随机推荐
- Oracle 对比两张表不一样 的数据
闲来无事,更一片博客,前几天有一个项目中有一个需求,用户通过excel导入数据,由于是通用的导入,所以导入的列的类型都为varchar,所以需要建一张中间表,使其列都为varchar类型,然后通过存储 ...
- L146 Space Station Hole Cause Will Be Determined
The head of the U.S. space agency said Tuesday he's sure that investigators will determine the cause ...
- iOS 11 实现App在禁止转屏的状态下网页播放器全屏
禁止转屏是这个意思,在General中设置Device Orientation只有竖屏. 要点就是重写UIViewController的以下3个属性方法 系统的全屏视频播放器是AVFullScreen ...
- java高并发下的数据安全
高并发下的数据安全 我们知道在多线程写入同一个文件的时候,会存现“线程安全”的问题(多个线程同时运行同一段代码,如果每次运行结果和单线程运行的结果是一样的,结果和预期相同,就是线程安全的).如果是My ...
- Vim技能修炼教程(1) - 使用vundle管理插件
世界上有两个伟大的编辑器:一个是emacs,一个是vi.它们从诞生以来,一直在Unix/Linux世界得到最广泛的支持. 尽管过了几十年,在Windows平台上和跨平台上有层出不穷的后起之秀不断挑战它 ...
- Emgu cv 学习笔记
http://www.cnblogs.com/CoverCat/p/5003363.html emgu中imagebox与picturebox imagebox 是emgu 设置好厚,新出现的控件 ...
- IPv4地址范围和一些小知识
IP地址范围: 保留地址(私有IP地址): 10.0.0.0——10.255.255.255 172.16.0.0——172.31.255.255 192.168.0.0——192.1 ...
- Quartz 2D编程指南(7) - 阴影(Shadows)
阴影是绘制在一个图形对象下的且有一定偏移的图片,它用于模拟光源照射到图形对象上所形成的阴影效果,如果7-1所示.文本也可以有阴影.阴影可以让一幅图像看上去是立体的或者是浮动的. 阴影有三个属性: 1. ...
- github上对一些名词的理解(之如fork)
fork: Fork 的本义是 叉子(名词) . 比较自然的引申成 分叉(动词) ,就像上面叉子,从左到从右,一条线变成多条了. Git/GitHub 用户下面的图 来表达 Fork:分叉.克隆 出一 ...
- 隐藏控件HiddenField使用
HiddenField控件顾名思义就是隐藏输入框的服务器控件,它能让你保存那些不需要显示在页面上的且对安全性要求不高的数据. 增加HiddenField,其实是为了让整个状态管理机制的应用程度更加全面 ...