今天在看同事新买到的《C#本质论 Edition 4》的时候,对比下以前Edtion3的新特性时针对Async/Await关键字时发现对一些线程方面的定义还理解的不是很透彻,脉络还不是很清晰,这样有了本文,希望对有同样困惑的朋友有些帮助。

文中部分内容摘取自《Essential C# 5.0 Edition 4》,还有一些我个人的对线程方面知识的理解与概括,如果有错误的地方还请指出,如果您觉得文章还不错,请点击“推荐” :)

C#线程模型脉络

缩写:

SPM:Synchronous Programming Mode,同步编程模型。

APM:Asynchronous Programming Mode,异步编程模型。

EAP:Event-Based Asynchronous Programming,基于事件的异步编程。

TAP:Task-Based Asynchronous Programming,基于任务的异步编程。

C#的编程模型从SPM发展到APM编程模型,是Winform发展过程中解决界面由于同步执行长时间任务而导致界面“卡住”而发展而来(这里插一句个人对技术的理解:技术的发展总是在旧事物不能得到满足的情况下发展而来,新事物总是在特定的场合“出现”,并更好的代替了旧事物,所以,如果想了深入了解一个新事物的特性,必然要了解其发展脉络以及适用的场景),有了异步编程模式之后,人们又开始探索如何能更好的管理线程,更简单的适用,于是有了类似Background Worker和async/await一类的特性。

C#线程的编程模型到这里也就结束了,如果你有这方面的经验可能都觉得以上都是“废话”,或者“一言以概之”,的确,我不反对,但是有时候写文章有些时候不仅仅是炫耀技术,更多的是总结、归纳,理解的东西不一定说的出来,说出来的东西不一定真正理解!

下面,针对每个内容做一个小结,老鸟飞过:)

SPM同步编程模型

Winform程序基于消息泵机制,依次执行从“消息泵”中取出的数据”,当某一消息需耗费大量系统资源去执行,则下一条消息不得不等待,这样会造成程序界面“挂起”,这就是同步执行任务的一种模型,当然这只是同步模型下的一种劣势,其实现实中我们同步模型我们无时无刻不在用,包括编写的代码、执行的业务逻辑等。

APM异步编程模型

为了解决同步模型中由于执行耗时任务而不得不等待的问题,产生了异步编程模式,其核心思想就是将耗时任务交付给其他“线程”去做,释放主线程。这里多出两句,在操作系统级别上Windows采用分时、多核以及多线程等技术来充分利用系统资源;在C#语言上由Thread发展到Task,Task是更高级的对Thread封装,提升资源利用率,有了这些基础才有更广阔的空间。

BeginXXX和EndXXX

使用BeginXXX和EndXXX实现基本的异步模式。

    class Program
{
static void Main(string[] args)
{
string url = "http://www.cnblogs.com/cuiyansong/";
if (args.Length > )
url = args[];
Console.WriteLine(url);
var webRequest = System.Net.WebRequest.Create(url);
IAsyncResult asyncResult = webRequest.BeginGetResponse(null, null); while (!asyncResult.AsyncWaitHandle.WaitOne())
{
Console.WriteLine(".");
} var webResponse = webRequest.EndGetResponse(asyncResult);
using (System.IO.StreamReader reader = new System.IO.StreamReader(webResponse.GetResponseStream()))
{
var bytes = Encoding.UTF8.GetBytes(reader.ReadToEnd());
int length = bytes.Length;
Console.WriteLine(length);
} Console.Read();
}
}

TPL+APM模式

TPL:Task Parallel Library 任务并行库,其对task进行扩展,类似于ThreadPool。

这个模式是利用TaskFactory进行并行计算(下载)的例子,这个例子很有意思,很复古的感觉,有情趣的拷贝代码看看运行效果~~~

    class Program
{
private static object ConsoleSyncObj = new object();
static void Main(string[] args)
{
string[] urls = args;
if (args.Length == )
{
urls = new string[]
{
"http://www.cnblogs.com/cuiyansong/",
"https://github.com/Cuiyansong",
"http://www.cnblogs.com/",
};
} Task[] tasks = new Task[urls.Length];
for (int i = ; i < tasks.Length; i++)
{
tasks[i] = DisplayPageSizeAsync(urls[i], i);
} while (!Task.WaitAll(tasks, ))
{
DisplayProgress(tasks);
}
Console.SetCursorPosition(, urls.Length);
Console.Read();
} private static void DisplayProgress(Task[] tasks)
{
for (int i = ; i < tasks.Length; i++)
{
if (!tasks[i].IsCompleted)
{
DisplayProgress((WebRequestState)tasks[i].AsyncState);
}
}
} private static void DisplayProgress(WebRequestState state)
{
lock (ConsoleSyncObj)
{
int left = state.ConsoleColumn;
int top = state.ConsoleLine;
if (left > Console.BufferWidth - int.MaxValue.ToString().Length)
{
left = state.Url.Length;
Console.SetCursorPosition(left, top);
Console.Write("".PadRight(Console.BufferWidth - state.Url.Length));
state.ConsoleColumn = left;
}
Write(state, ".");
}
} private static string FormatBytes(long bytes)
{
string[] magintudes = new string[] { "GB", "MB", "KB", "Bytes" };
long max = (long)Math.Pow(, magintudes.Length); return string.Format("{1:##.##} {0}", magintudes.FirstOrDefault(mag => bytes > (max /= )) ?? "0 Bytes", (decimal)bytes / (decimal)max).Trim();
} private static Task<System.Net.WebResponse> DisplayPageSizeAsync(string url, int i)
{
var webRequest = System.Net.WebRequest.Create(url);
var requestState = new WebRequestState(webRequest, i); Write(requestState, url + " "); return Task<System.Net.WebResponse>.Factory.FromAsync(webRequest.BeginGetResponse, GetResponseAsyncCompleted, requestState);
} private static WebResponse GetResponseAsyncCompleted(IAsyncResult asyncResult)
{
WebRequestState completedState = (WebRequestState)asyncResult.AsyncState;
HttpWebResponse response = (HttpWebResponse)completedState.WebRequest.EndGetResponse(asyncResult); using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
int length = reader.ReadToEnd().Length;
Write(completedState, FormatBytes(length));
}
return response;
} private static void Write(WebRequestState completedState, string text)
{
lock (ConsoleSyncObj)
{
Console.SetCursorPosition(completedState.ConsoleColumn, completedState.ConsoleLine);
Console.Write(text);
completedState.ConsoleColumn += text.Length;
}
} private class WebRequestState
{
public System.Net.WebRequest WebRequest { get; private set; } public int ConsoleLine { get; set; } public int ConsoleColumn { get; set; } public string Url
{
get
{
return WebRequest.RequestUri.ToString();
}
} public WebRequestState(System.Net.WebRequest request)
{
WebRequest = request;
} public WebRequestState(System.Net.WebRequest request, int line)
{
WebRequest = request;
ConsoleLine = line;
ConsoleColumn = ;
}
}
}

BackgroundWorker模型

针对经常出现任务委托后,执行任务并返回进度的需求,提供了BackgroundWorker类,节省开发时间。

    class Program
{
static void Main(string[] args)
{
/*****************************************************************************
* 由于是演示,并为增加对BackgroundWorker取消等功能,这里只是简单演示。
* 详细请参考:http://www.cnblogs.com/RoyYu/archive/2011/08/10/2133309.html
* ***************************************************************************/ System.ComponentModel.BackgroundWorker worker = new System.ComponentModel.BackgroundWorker();
worker.WorkerReportsProgress = true;//报告完成进度
worker.WorkerSupportsCancellation = true;//允许用户终止后台线程 worker.DoWork += (sender, e) =>
{
for (int i = ; i < ; i++)
{
System.Threading.Thread.Sleep();
worker.ReportProgress(i, i);
}
};
worker.ProgressChanged += (sender, e) =>
{
Console.WriteLine(string.Format("完成百分比。。。{0}", e.ProgressPercentage / (float)));
};
worker.RunWorkerCompleted += (sender, e) =>
{
if (!e.Cancelled && e.Error == null)
{
Console.WriteLine("处理成功,请按任意键返回。");
} else
{
Console.WriteLine("处理中断,请按任意键返回。");
}
};
worker.RunWorkerAsync();
Console.Read();
}
}

Async/Await模型

这回用窗体程序进行演示,对比起来效果会更明显些。

    public partial class MainWindow : Window
{
List<string> urls = new List<string>();
public MainWindow()
{
InitializeComponent(); urls = new List<string>()
{
"www.baidu.com",
"www.cnblogs.cn",
"www.stackoverflow.com",
};
} /// <summary>
/// 同步执行示例:当点击Button后,界面挂起,等待执行完毕后显示全部内容。
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Button_Click1(object sender, RoutedEventArgs e)
{
InfoList.Text = "Ping....." + System.Environment.NewLine;
Ping p = new Ping(); foreach (var item in urls)
{
PingReply pingRelay = p.Send(item);
InfoList.Text += string.Format("Host Name: {0},Roundtrip Time: {1},Status: {2}", item, pingRelay.RoundtripTime.ToString(), pingRelay.Status + System.Environment.NewLine);
}
}
/// <summary>
/// 异步执行示例:当点击Button后,界面不挂起,界面滚动显示信息。
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void Button_Click2(object sender, RoutedEventArgs e)
{
InfoList.Text = "Ping....." + System.Environment.NewLine;
Ping p = new Ping(); foreach (var item in urls)
{
PingReply pingRelay = await p.SendPingAsync(item);
InfoList.Text += string.Format("Host Name: {0},Roundtrip Time: {1},Status: {2}", item, pingRelay.RoundtripTime.ToString(), pingRelay.Status + System.Environment.NewLine);
}
}
}

结语

下载源代码,请点击这里

文章内容确实不是很深入,但对理解Winform的异步线程模型还是很有帮助的,今后有时间会针对线程单独来分享,希望各位看官不吝赐“赞”,水平有限,有问题欢迎提问:)

引用

  1. 文中部分例子出自《Essential C# 5.0 Edition 4》
  2. BackgrounWorker例子:http://www.cnblogs.com/RoyYu/archive/2011/08/10/2133309.html

C#线程模型脉络的更多相关文章

  1. 看我是如何处理自定义线程模型---java

    看过我之前文章的园友可能知道我是做游戏开发,我的很多思路和出发点是按照游戏思路来处理的,所以和web的话可能会有冲突,不相符合. 来说说为啥我要自定义线程模型呢? 按照我做的mmorpg或者mmoar ...

  2. HBase的Write Ahead Log (WAL) —— 整体架构、线程模型

    解决的问题 HBase的Write Ahead Log (WAL)提供了一种高并发.持久化的日志保存与回放机制.每一个业务数据的写入操作(PUT / DELETE)执行前,都会记账在WAL中. 如果出 ...

  3. Netty学习三:线程模型

    1 Proactor和Reactor Proactor和Reactor是两种经典的多路复用I/O模型,主要用于在高并发.高吞吐量的环境中进行I/O处理. I/O多路复用机制都依赖于一个事件分发器,事件 ...

  4. Mina、Netty、Twisted一起学(十):线程模型

    要想开发一个高性能的TCP服务器,熟悉所使用框架的线程模型非常重要.MINA.Netty.Twisted本身都是高性能的网络框架,如果再搭配上高效率的代码,才能实现一个高大上的服务器.但是如果不了解它 ...

  5. WPF QuickStart系列之线程模型(Thread Model)

    这篇博客将介绍WPF中的线程模型. 首先我们先来看一个例子,用来计算一定范围内的素数个数. XAML: <Grid> <Grid.RowDefinitions> <Row ...

  6. servlet的生命周期与运行时的线程模型

    第 14 章 生命周期 注意 讲一下servlet的生命周期与运行时的线程模型,对了解servlet的运行原理有所帮助,这样才能避免一些有冲突的设计. 如果你不满足以下任一条件,请继续阅读,否则请跳过 ...

  7. eventloop & actor模式 & Java线程模型演进 & Netty线程模型 总结

    eventloop的基本概念可以参考:http://www.ruanyifeng.com/blog/2013/10/event_loop.html Eventloop指的是独立于主线程的一条线程,专门 ...

  8. 理解RxJava线程模型

    RxJava作为目前一款超火的框架,它便捷的线程切换一直被人们津津乐道,本文从源码的角度,来对RxJava的线程模型做一次深入理解.(注:本文的多处代码都并非原本的RxJava的源码,而是用来说明逻辑 ...

  9. Hbase WAL线程模型源码分析

    版权声明:本文由熊训德原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/257 来源:腾云阁 https://www.qclo ...

随机推荐

  1. CGI(通用网关接口)

    公共网关接口 CGI(Common Gateway Interface) 是WWW技术中最重要的技术之一,有着不可替代的重要地位.CGI是外部应用程序(CGI程序)与Web服务器之间的接口标准,是在C ...

  2. Mac/Linux 定时运行命令行

    想要开机运行的话可以通过 mac 自带的 Automator 将要运行的命令打包成一个app,用后在用户组的“登录时启动”列表里加上那个app. 但是想要定时运行就不能这么做了,要用上一个叫cront ...

  3. php session文件修改路径

    默认状态下php的 sess_文件会生成到/tmp目录下,1天的时间就会生成很多,由于/tmp目录下还有别的重要文件,所以看起来不爽.具体更改做法是,找到 php.ini文件里面的session.sa ...

  4. 分析递归式 Solving Recurrences------GeeksforGeeks 翻译

    在上一章中我们讨论了如何分析循环语句.在现实中,有很多算法是递归的,当我们分析这些算法的时候我们要找到他们的的递归关系.例如归并排序,为了排序一个数组,我们把它平均分为两份然后再重复平分的步骤.最后我 ...

  5. Golang tool:include spider library,image library and some other db library such as mysql,redis,mogodb,hbase,cassandra

    一.Go_tool This is a tool library for Golang.Dont't worry about not understant it! All comment writes ...

  6. GitHub Top 100的Android开源库

    摘要: 本项目主要对目前 GitHub 上排名前 100 的 Android 开源库进行简单的介绍, 至于排名完全是根据GitHub搜索Java语言选择「Best M... 本项目主要对目前 GitH ...

  7. MySQL数据库学习笔记(六)----MySQL多表查询之外键、表连接、子查询、索引

    本章主要内容: 一.外键 二.表连接 三.子查询 四.索引 一.外键: 1.什么是外键 2.外键语法 3.外键的条件 4.添加外键 5.删除外键 1.什么是外键: 主键:是唯一标识一条记录,不能有重复 ...

  8. 更改QTP默认测试脚本路径

    QTP的默认测试脚本路径为安装路径下的Tests文件夹下, 如果你安装在D:,那么默认脚本路径为D:\Program Files\HP\QuickTest Professional\Tests 但是因 ...

  9. java 12 - 5 带有缓冲区的字符流

    字符流为了高效读写,也提供了对应的字符缓冲流. 字符缓冲流:A. BufferedWriter:字符缓冲输出流 B. BufferedReader:字符缓冲输入流 A.BufferedWriter:字 ...

  10. java 20 - 6 加入了异常处理的字节输出流的操作

    昨天坐了十几个钟的车回家,累弊了.... ————————————割掉疲劳————————————— 前面的字节输出流都是抛出了异常不管,这次的加入了异常处理: 首先还是创建一个字节输出流对象,先给它 ...