阅读《LEARNING HARD C#学习笔记》知识点总结与摘要五
本篇文章主要是总结异步编程的知识点,也是本系列的最后一篇文章,每一个知识点我都有写出示例代码,方便大家理解,若发现有误或不足之处还请指出,由于书中作者对此知识点讲解过于简单,所以在写这篇文章时本人参考与学习了网上许多大牛们的经验,在此感谢那些愿意分享的人们,谢谢!
二十三.异步编程
APM(异步编程模型):若类实现了返回类型为IAsyncResult接口的BeginXXX方法和EndXXX方法,则表明该类支持异步编程模型。如:委托类型定义了BeginInvoke与EndInvoke方法,所以所有的委托类型都实现了异步编程模型;
调用方法代码如下(以读取文件内容为例):
第一种方法(先调用BeginRead方法,再调用EndRead方法):
FileStream fs = new FileStream("文件路径", FileMode.Open);
byte[] data = new byte[fs.Length];
IAsyncResult result = fs.BeginRead(data, 0, data.Length,null, null);
fs.EndRead(result);
fs.Close();
fs.Dispose();
System.Text.UTF8Encoding UTF8 = new System.Text.UTF8Encoding();
string readContent = UTF8.GetString(data);
Console.WriteLine(readContent);
Console.Read();
注意:调用EndRead方法时,会阻塞当前调用的线程,直到处理完成,若在UI线程中调用,将会出现UI假死现象;
第二种方法(先调用BeginRead方法,再通过IAsyncResult. AsyncWaitHandle得到WaitHandle对象,然后执行WaitHandle. WaitOne方法来等待异步执行完成,最后执行EndRead方法):
FileStream fs = new FileStream("文件路径", FileMode.Open);
byte[] data = new byte[fs.Length];
IAsyncResult result = fs.BeginRead(data, 0, data.Length, null, null);
WaitHandle readWait=result.AsyncWaitHandle;
readWait.WaitOne();
fs.EndRead(result);
System.Text.UTF8Encoding UTF8 = new System.Text.UTF8Encoding();
string readContent = UTF8.GetString(data);
Console.WriteLine(readContent);
Console.Read();
注意:调用WaitOne方法时,会阻塞当前调用的线程,直到处理完成,若在UI线程中调用,将会出现UI假死现象;
第三种方法(先调用BeginRead方法,再循环判断IAsyncResult. IsCompleted,最后执行EndRead方法):
FileStream fs = new FileStream("文件路径", FileMode.Open);
byte[] data = new byte[fs.Length];
IAsyncResult result = fs.BeginRead(data, 0, data.Length, null, null);
while(!result.IsCompleted)
{
Thread.Sleep(1000);
}
fs.EndRead(result);
System.Text.UTF8Encoding UTF8 = new System.Text.UTF8Encoding();
string readContent = UTF8.GetString(data);
Console.WriteLine(readContent);
Console.Read();
注意:循环判断IsComplete方法时,会阻塞当前调用的线程,直到处理完成[即:IsCompleted=true],若在UI线程中循环判断,将会出现UI假死现象;
第四种方法(先定义回调方法,然后再调用BeginRead方法,调用时传入异步回调方法委托实例及相关数据):
FileStream fs = new FileStream("文件路径", FileMode.Open);
byte[] data = new byte[fs.Length];
IAsyncResult result = fs.BeginRead(data, 0, data.Length, new AsyncCallback(ReadCallback), data);
Console.Read();
//回调方法
static void ReadCallback(IAsyncResult ar)
{
WaitHandle readWait = ar.AsyncWaitHandle;
byte[] data =( byte[])ar.AsyncState;
System.Text.UTF8Encoding UTF8 = new System.Text.UTF8Encoding();
string readContent = UTF8.GetString(data);
Console.WriteLine(readContent);
}
说明:第四种由于采用异步委托方法获取得结果,所以不会阻塞调用线程,也就不会出现UI假死现象,当然前面三种方法也可以将耗时逻辑(等待及获取结果)放在方法中,然后在执行时开启新线程,这样可以达到与第四种相同的方法,但个人建议若在UI主线程中实现异步调用,优先考虑采用第四种方法;
EAP(基于事件的异步模式):实现了EAP的类具有一个或多个以Async为后缀的方法,以及对应的Completed事件,并支持异步方法的取消与进度报告。
EAP通过事件、AsyncOperationManager类和AsyncOperation类两个帮助器类实现如下功能:
1) 异步执行耗时的任务。
2) 获得进度报告和增量结果。
3) 支持耗时任务的取消。
4) 获得任务的结果值或异常信息。
5) 更复杂:支持同时执行多个异步操作、进度报告、增量结果、取消操作、返回结果值或异常信息。
对于相对简单的多线程应用程序,BackgroundWorker组件提供了一个简单的解决方案。对于更复杂的异步应用程序,可以考虑实现一个符合基于事件的异步模式的类。
可参见这篇博文:http://www.cnblogs.com/heyuquan/archive/2013/04/01/2993085.html
以下是BackgroundWorker组件在控制台程序中的使用方法:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace ConsoleApplication1
{
class Program
{
static int Main(string[] args)
{
Console.Write("Main Thread ID:{0}\n", System.Threading.Thread.CurrentThread.ManagedThreadId);
int workTimes = 100;
BackgroundWorker bgWorker = new BackgroundWorker();
bgWorker.WorkerSupportsCancellation = true;//设置支持异步取消
bgWorker.WorkerReportsProgress = true;//设置支持报告进度 bgWorker.DoWork += bgWorker_DoWork;
bgWorker.ProgressChanged += bgWorker_ProgressChanged;
bgWorker.RunWorkerCompleted += bgWorker_RunWorkerCompleted;
bgWorker.RunWorkerAsync(workTimes);//启动异步执行
string input = Console.ReadLine();
if (input == "c" && bgWorker.IsBusy)
{
bgWorker.CancelAsync();
}
Console.Read();
return 0;
} static void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
//在新线程中处理该方法
BackgroundWorker bgWorker = sender as BackgroundWorker;
int workTimes = Convert.ToInt32(e.Argument);
for(int i = 0; i <= workTimes;i++ )
{
if (bgWorker.CancellationPending) //如果取消了
{
e.Cancel = true;
Console.Write("ThreadID:{0}-->Cancel!\n", System.Threading.Thread.CurrentThread.ManagedThreadId);
break;
}
else
{
Console.Write("Thread ID:{0}-->WorkTimes:{1}\n", System.Threading.Thread.CurrentThread.ManagedThreadId, i);
bgWorker.ReportProgress(i);//投告进度,引发ProgressChanged事件
System.Threading.Thread.Sleep(1000);
}
}
} static void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//当进度改变时在新线程中调用该方法,可直接操作控件
Console.Write("Thread ID:{0}-->Progress:{1}\n", System.Threading.Thread.CurrentThread.ManagedThreadId, e.ProgressPercentage);
} static void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//当DoWork方法处理完成后(不论是否为取消)在新线程中调用此方法
Console.Write("Thread ID:{0}-->Completed!", System.Threading.Thread.CurrentThread.ManagedThreadId);
}
}
}
TAP(基于任务的异步模式):一般使用Task类来实现基于任务的异步模式编程,实现步骤如下:
- 通过调用Task类指定的构造函数来实例化Task对象,如:Task task = new Task(Action委托实例,CancellationToken实例);注意:Task类有许多重载的构造函数,可依据实际情况进行调用
- 调用Task实例对象的Start方法启动异步执行构造函数参数中Action委托实例所引用的方法,如:task.Start()
可参见这篇博文:http://www.cnblogs.com/heyuquan/archive/2013/04/18/Task-based-Asynchronous-Pattern.html
具体的使用代码示例如下(为了让新手也能看懂,所以此处我采用标准的委托实例方法来写,大家可以使用Lambda表达式来写,那样代码量就会精简一些):
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms; namespace WindowsFormsApplication1
{
public partial class Form4 : Form
{
CancellationTokenSource cts = new CancellationTokenSource(); //用于发出与接收任务取消信息
public Form4()
{
InitializeComponent();
} private void Form4_Load(object sender, EventArgs e)
{
TestTAP();
} private void TestTAP()
{
WriteMsg(string.Format( "Main Thread ID:{0}", Thread.CurrentThread.ManagedThreadId));
SynchronizationContext syncContext = SynchronizationContext.Current; //获取当前同步上下文,用于在异步中可操作UI上的控件等
Task task = new Task(new Action<object>(TAPAsyncMethod), syncContext);
task.Start();
} private void TAPAsyncMethod(object arg)
{
CancellationToken ct = cts.Token;
SynchronizationContext sc = arg as SynchronizationContext; SendOrPostCallback callback = new SendOrPostCallback(WriteMsg);
int threadId = Thread.CurrentThread.ManagedThreadId; for (int i = 1; i <= 100; i++)
{
if (ct.IsCancellationRequested)
{
sc.Post(callback, string.Format("Thread ID:{0}发出消息:任务取消了!", threadId));
break;
}
int n = i + (i+1);
sc.Post(callback, string.Format("Thread ID:{0},发出消息:第{1}次执行结果:{2}", threadId, i, n));
Thread.Sleep(500);
}
} private void WriteMsg(object state)
{
listBox1.Items.Add(string.Format( "Thread ID:{0}显示:[{1}]", Thread.CurrentThread.ManagedThreadId, state));
listBox1.SelectedIndex = listBox1.Items.Count - 1;
} private void button1_Click(object sender, EventArgs e)
{
//发送取消命令
cts.Cancel();
}
}
}
C# 5.0中使用async和await关键字来实现异步编程,在这两个关键字的帮助下,我们可以使用同步编程的思想方式来实现异步编程,定义异步方法只需在定义普通方法的前面加上async关键字,在方法体指定的语句前加上await关键字实现异步处理耗时的代码。async和await关键字不会让调用的方法运行在新线程中,而是将方法以await关键字来作为界限分割成多个片段,并使其中的一些片段以异步方式执行。
注意:
1.只有标记了async关键字的方法或lambda表达式才能在方法体中使用await关键字;
2. 方法用async关键字标记不会影响方法是同步还是异步运行并完成,即:若方法用async关键字,但方法体中并没有包含await关键字,则仍以同步方式运行并完成;
3.只有返回类型为void、Task或Task<TResult>的方法才能用async关键字标记,但除如下情况:
A.程序的入口方法(Main方法);B.标记为同步的方法([MethodImpl(MethodImplOptions.Synchronized)]);C.包含ref或out参数的方法;D. Lambda被用作表达式树时;
4.只有方法的返回类型为可等待的类型(即类型必需包含公共的GetAwaiter() 方法并且返回有效的TaskAwaiter,如:Task、Task<TResult>)才能用await关键字标记;
可参见这篇博文:http://www.cnblogs.com/heyuquan/archive/2012/11/30/2795859.html
具体的使用代码示例如下:
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms; namespace WindowsFormsApplication1
{
public partial class Form5 : Form
{
public Form5()
{
InitializeComponent();
} private void button1_Click(object sender, EventArgs e)
{
listBox1.Items.Add(string.Format("Main Thread ID:{0}", Thread.CurrentThread.ManagedThreadId));
AsyncMethod();
} private async void AsyncMethod()
{
int threadId = Thread.CurrentThread.ManagedThreadId;
SynchronizationContext syncContext = SynchronizationContext.Current;
await ExecBody(syncContext);//此处会开新线程,并且下面的代码将在该处执行完成后才会执行。
listBox1.Items.Add("方法执行完成了!");
listBox1.SelectedIndex = listBox1.Items.Count - 1;
} private Task ExecBody(SynchronizationContext sc)
{
return Task.Run(() =>
{
int threadId = Thread.CurrentThread.ManagedThreadId;
for (int i = 1; i <= 100; i++)
{
int n = i + (i + 1);
sc.Post(state =>
{
listBox1.Items.Add(string.Format("Thread ID:{0}显示:[{1}]", Thread.CurrentThread.ManagedThreadId, state));
listBox1.SelectedIndex = listBox1.Items.Count - 1;
}, string.Format("Thread ID:{0},发出消息:第{1}次执行结果:{2}", threadId, i, n));
Thread.Sleep(500);
}
});
}
}
}
阅读《LEARNING HARD C#学习笔记》知识点总结与摘要五的更多相关文章
- 阅读《LEARNING HARD C#学习笔记》知识点总结与摘要系列文章索引
从发表第一篇文章到最后一篇文章,时间间隔有整整一个月,虽只有5篇文章,但每一篇文章都是我吸收<LEARNING HARD C#学习笔记>这本书的内容要点及网上各位大牛们的经验,没有半点废话 ...
- 阅读《LEARNING HARD C#学习笔记》知识点总结与摘要三
最近工作较忙,手上有几个项目等着我独立开发设计,所以平时工作日的时候没有太多时间,下班累了就不想动,也就周末有点时间,今天我花了一个下午的时间来继续总结与整理书中要点,在整理的过程中,发现了书中的一些 ...
- 阅读《LEARNING HARD C#学习笔记》知识点总结与摘要二
今天继续分享我的阅读<LEARNING HARD C#学习笔记>知识点总结与摘要二,仍然是基础知识,但可温故而知新. 七.面向对象 三大基本特性: 封装:把客观事物封装成类,并隐藏类的内部 ...
- 阅读《LEARNING HARD C#学习笔记》知识点总结与摘要一
本人有幸在Learning Hard举行的整点抢书活动<Learninghard C#学习笔记>回馈网友,免费送书5本中免费获得了一本<LEARNING HARD C#学习笔记> ...
- Deep learning with Python 学习笔记(11)
总结 机器学习(machine learning)是人工智能的一个特殊子领域,其目标是仅靠观察训练数据来自动开发程序[即模型(model)].将数据转换为程序的这个过程叫作学习(learning) 深 ...
- Deep learning with Python 学习笔记(10)
生成式深度学习 机器学习模型能够对图像.音乐和故事的统计潜在空间(latent space)进行学习,然后从这个空间中采样(sample),创造出与模型在训练数据中所见到的艺术作品具有相似特征的新作品 ...
- Deep learning with Python 学习笔记(9)
神经网络模型的优化 使用 Keras 回调函数 使用 model.fit()或 model.fit_generator() 在一个大型数据集上启动数十轮的训练,有点类似于扔一架纸飞机,一开始给它一点推 ...
- Deep learning with Python 学习笔记(8)
Keras 函数式编程 利用 Keras 函数式 API,你可以构建类图(graph-like)模型.在不同的输入之间共享某一层,并且还可以像使用 Python 函数一样使用 Keras 模型.Ker ...
- Deep learning with Python 学习笔记(7)
介绍一维卷积神经网络 卷积神经网络能够进行卷积运算,从局部输入图块中提取特征,并能够将表示模块化,同时可以高效地利用数据.这些性质让卷积神经网络在计算机视觉领域表现优异,同样也让它对序列处理特别有效. ...
- Deep learning with Python 学习笔记(6)
本节介绍循环神经网络及其优化 循环神经网络(RNN,recurrent neural network)处理序列的方式是,遍历所有序列元素,并保存一个状态(state),其中包含与已查看内容相关的信息. ...
随机推荐
- 年底了,特贡献一些C#有意思的算法题
2013年,即将要过去了.屌丝C#程序员们拿到了年终奖不?是不是又想蠢蠢欲动了?是不是想通过跳槽来为自己实现加薪的梦想?好吧,跳槽之前还是做点准备吧,准备好C#的笔试吧.这里我收集了些奉献给大家,大家 ...
- 【CefSharp】 禁用右键菜单 与 控制弹出窗口的方式(限版本39.0.0.1)
这周没什么时间,一开始就在忙一些CefSharp的事情,Win10的研究就放了下来,CefSharp的资料挺少的,但好在是开源的,可以我们便宜的折腾.因为两个的内容都不多,我就合成一篇文章啦. 这还里 ...
- CQRS\ES架构介绍
大家好,我叫汤雪华.我平时工作使用Java,业余时间喜欢用C#做点开源项目,如ENode, EQueue.我个人对DDD领域驱动设计.CQRS架构.事件溯源(Event Sourcing,简称ES). ...
- Lock-Free 编程
文章索引 Lock-Free 编程是什么? Lock-Free 编程技术 读改写原子操作(Atomic Read-Modify-Write Operations) Compare-And-Swap 循 ...
- 设计模式之美:Iterator(迭代器)
索引 意图 结构 参与者 适用性 效果 相关模式 实现 实现方式(一):Iterator 模式结构样式代码. 实现方式(二):实现 IEnumerable 中序遍历二叉树. 实现方式(三):实现 Bi ...
- Json序列化之.NET开源类库Newtonsoft.Json的研究
一.Json简介 JSON(全称为JavaScript Object Notation) 是一种轻量级的数据交换格式.它是基于JavaScript语法标准的一个子集. JSON采用完全独立于语言的文 ...
- Android多线程分析之二:Thread的实现
Android多线程分析之二:Thread的实现 罗朝辉 (http://www.cnblogs.com/kesalin/) CC 许可,转载请注明出处 在前文<Android多线程分析之一 ...
- js模版引擎handlebars.js实用教程——with-进入到某个属性(进入到某个上下文环境)
返回目录 <!DOCTYPE html> <html> <head> <META http-equiv=Content-Type content=" ...
- 实战使用Axure设计App,使用WebStorm开发(3) – 构建页面架构
系列文章 实战使用Axure设计App,使用WebStorm开发(1) – 用Axure描述需求 实战使用Axure设计App,使用WebStorm开发(2) – 创建 Ionic 项目 实战使 ...
- 每天一个linux命令(52):ifconfig命令
许多windows非常熟悉ipconfig命令行工具,它被用来获取网络接口配置信息并对此进行修改.Linux系统拥有一个类似的工具,也就是ifconfig(interfaces config).通常需 ...