多线程与异步编程可以达到避免调用线程异步阻塞作用,但是两者还是有点不同。

多线程与异步编程的异同

1.线程是cpu 调度资源和分配的基本单位,本质上是进程中的一段并发执行的代码。

2.线程编程的思维符合正常人的思维习惯,线程中的处理程序依然是顺序执行,所以编程起来比较方便,但是缺点也是明显的,多线程的使用会造成多线程之间的上下文切换带来系统花销,并且共享变量之间也是会造成死锁的问题。

3.因为异步操作无须额外的线程负担,并且使用回调的方式进行处理,在设计良好的情况下,处理函数可以不必使用共享变量(即使无法完全不用,最起码可以减少 共享变量的数量),减少了死锁的可能。当然异步操作也并非完美无暇。编写异步操作的复杂程度较高,程序主要使用回调方式进行处理,与普通人的思维方式有些出入,而且难以调试。

适用范围

  在了解了线程与异步操作各自的优缺点之后,我们可以来探讨一下线程和异步的合理用途。我认为:当需要执行I/O操作时,使用异步操作比使用线程+同步 I/O操作更合适。I/O操作不仅包括了直接的文件、网络的读写,还包括数据库操作、Web Service、HttpRequest以及.net Remoting等跨进程的调用。

  而线程的适用范围则是那种需要长时间CPU运算的场合,例如耗时较长的图形处理和算法执行。但是往往由于使用线程编程的简单和符合习惯,所以很多朋友往往会使用线程来执行耗时较长的I/O操作。这样在只有少数几个并发操作的时候还无伤大雅,如果需要处理大量的并发操作时就不合适了。

二、多线程编程

刚好这段时间在看网络编程,在这里就结合多线程和网络编程,实现能够应对多客户端请求的服务端。Socket 类的 Accept() 方法一直等待,直到有客户端连接请求。Socket 网络编程可以参考C# 网络编程之 Socket 编程 。

C# 中有专门的异步网络编程方法,具体可以参考 几种Socket服务器模型比较。

多线程实现一个并发服务器的例子:

static Socket client;
static void Main(string[] args)
{
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 9060); //创建 IPEndPoint 对象,表示接受任何端口 9050 的客户机地址
Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); // TCP
server.Bind(ipep); //绑定
server.Listen(20); //监听
Console.WriteLine("正在监听...");

//下面这段代码阻塞,可以用新线程执行,但可能会出问题
while (true)
{
client = server.Accept(); //收到客户端请求

//开新线程发送数据
Thread recvthread = new Thread(sendData);
recvthread.IsBackground = true; //后台线程
// 启动消息服务线程
recvthread.Start();
///也可以开其他线程,如接收数据线程
  }
}

static private void sendData()
{
if (client != null)
{
Console.WriteLine("客户机" + client.RemoteEndPoint + "连接到服务器");
string data = "hello client";
byte[] msg = System.Text.Encoding.ASCII.GetBytes(data); //将 string 转化为 byte 数组
client.Send(msg); //向客户端发生数据
Console.WriteLine("发生数据:" + data);
client.Close();
}
}

三、异步编程

基于异步的编程方法有三种:

  • 异步编程模式(APM),其中异步操作要求 Begin 和 End 方法(例如,异步写操作的 BeginWrite 和 EndWrite)。对于新的开发工作不再建议采用此模式。
  • 基于事件的异步模式 (EAP) 需要一个具有 Async 后缀的方法,还需要一个或多个事件、事件处理程序、委托类型和 EventArg 派生的类型,在 .NET Framework 2.0 版中引入的。对于新的开发工作不再建议采用此模式。
  • 基于任务的异步模式 (TAP),该模式使用一个方法表示异步操作的启动和完成。.NET Framework 4 中引入了 TAP,并且是 .NET Framework 中异步编程的建议方法。

Task 异步,有以下三种方法创建 Task:

  • Task.Factory.StartNew,比较常用。

  • Task.Run,是.net 4.5中增加的。

  • Task.FromResult,如果结果是已计算,就可以使用这种方法来创建任务。

下面就以 Task.Factory.StartNew 进行异步编程实现一个并发服务器的例子:

static Socket client;
static void Main(string[] args)
{
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 9060); //创建 IPEndPoint 对象,表示接受任何端口 9050 的客户机地址
Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); // TCP
server.Bind(ipep); //绑定
server.Listen(20); //监听
Console.WriteLine("正在监听...");

//下面这段代码阻塞,可以放到子线程执行,但是可能会出现问题,可以看最后面分析
while (true)
{
client = server.Accept();
//创建并启动 task
Task.Factory.StartNew(() =>
{
sendData(); //一个没有返回值的方法
});
}
}

static private void sendData()
{
if (client != null)
{
Console.WriteLine("客户机" + client.RemoteEndPoint + "连接到服务器");
string data = "hello client";
byte[] msg = System.Text.Encoding.ASCII.GetBytes(data); //将 string 转化为 byte 数组
client.Send(msg); //向客户端发生数据
Console.WriteLine("发生数据:" + data);
client.Close();
}
}

使用 Async 与 Await 进行异步编程

static Socket client;
static Socket server;
static void Main(string[] args)
{
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 9060); //创建 IPEndPoint 对象,表示接受任何端口 9050 的客户机地址
server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); // TCP
server.Bind(ipep); //绑定
server.Listen(20); //监听
Console.WriteLine("正在监听...");

accept();
}
static async void accept()
{
await acceptAsync();
}
static async Task acceptAsync() //异步接受请求
{
while (true)
{
client = server.Accept(); //收到客户端请求
await sendData();
}
}
static async Task sendData() //异步发生数据
{
if (client != null)
{
Console.WriteLine("客户机" + client.RemoteEndPoint + "连接到服务器");
string data = "hello client";
byte[] msg = System.Text.Encoding.ASCII.GetBytes(data); //将 string 转化为 byte 数组
client.Send(msg); //向客户端发生数据
//添加一个异步方法
await Task.Delay(1000);
Console.WriteLine("发生数据:" + data);
client.Close();
}
}

由于 Main() 函数不能设置为 async 模式,所以增加了一个accept 函数,使用 await 来执行异步操作 acceptAsync() ,等待接受客户端的请求。同时在异步操作 acceptAsync() 中执行异步 sendData() ,异步发送数据。

一个问题:在上面的三个程序中,采用多线程和Task.Factory.StartNew 实现服务端的两个程序,如果把下面两段代码作为子线程或者异步函数执行,本来是阻塞的函数 client = server.Accept(),却没有等待客户端连接,直接执行过去了??是因为 accept()在同时在静态函数和多线程中的关系????(因为在非静态函数中这样并没有问题),但是使用Async 与 Await 执行的异步函数却能正常执行。

while (true)
{
client = server.Accept(); //收到客户端请求

//开新线程发送数据
Thread recvthread = new Thread(sendData);
recvthread.IsBackground = true; //后台线程
// 启动消息服务线程
recvthread.Start();
///也可以开其他线程,如接收数据线程
}

while (true)
{
client = server.Accept();
//创建并启动 task
Task.Factory.StartNew(() =>
{
sendData(); //一个没有返回值的方法
});
}

初步谈谈 C# 多线程、异步编程与并发服务器的更多相关文章

  1. 多线程异步编程示例和实践-Thread和ThreadPool

    说到多线程异步编程,总会说起Thread.ThreadPool.Task.TPL这一系列的技术.总结整理了一版编程示例和实践,分享给大家. 先从Thread和ThreadPool说起: 1. 创建并启 ...

  2. 多线程异步编程示例和实践-Task

    上篇博文中,我们介绍了Thread和ThreadPool: 多线程异步编程示例和实践-Thread和ThreadPool 本文中我们继续,说一下TPL(Task Parallel Library, 简 ...

  3. C#——await与async实现多线程异步编程

    曾经,我们也许用过Thread.在主线程运行的时候.新开还有一个新线程,来运行新方法. 今天看别人发给我的一段代码的时候发现了一个不认识的await,可是又感觉非常熟悉的样子,感觉是线程那块儿的东西, ...

  4. 谈谈c#中异步编程模型的变迁

    大家在编程过程中都会用到一些异步编程的情况.在c#的BCL中,很多api都提供了异步方法,初学者可能对各种不同异步方法的使用感到迷惑,本文主要为大家梳理一下异步方法的变迁以及如何使用异步方法. Beg ...

  5. [.net 多线程]异步编程模式

    .NET中的异步编程 - EAP/APM 从.NET 4.5开始,支持的三种异步编程模式: 基于事件的异步编程设计模式 (EAP,Event-based Asynchronous Pattern) 异 ...

  6. socket编程和并发服务器

    socket这个词可以表示很多概念: 在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一标识网络通讯中的一个进程,“IP地址+端口号”就称为socket. 在TCP协议中,建立连接的两个进程 ...

  7. .Net 多线程 异步编程 Await、Async和Task

    await和async简介   await和async是在C#5中引入,并且在.NetFramewor4.5以及.NetCore中进行了支持.主要是解决性能瓶颈,并且增强系统的响应能力. msdn关于 ...

  8. Linux网络编程——tcp并发服务器(poll实现)

    想详细彻底地了解poll或看懂下面的代码请参考<Linux网络编程——I/O复用之poll函数> 代码: #include <string.h> #include <st ...

  9. C# 并发编程 (异步编程与多线程)

    并发:同时做多件事情 多线程:并发的一种形式,它采用多个线程来执行程序. 并行处理:把正在执行的大量的任务分割成小块,分配给多个同时运行的线程.并行处理是多线程的一种,而多线程是并发的一种. 异步编程 ...

随机推荐

  1. JDBC 基础

    JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口 ...

  2. Spring Data与elasticsearch版本对应关系

  3. PV IP UV

    PV(访问量) Page View,页面浏览量. 具体的说,就是在一天内,该网站的页面总共访问了多少次 IP(独立IP) 一天内访问网站的IP数量 UV(独立访客) Unique Visitor 一般 ...

  4. Yii2 场景

    下面给大家介绍一下 yii2.0 场景的使用. 现在在 post表里面有 title image content 三个的字段,当我创建一个 post 的时候,我想三个字段全部是必填项,但是你修改的时候 ...

  5. Java语言的分支

    JavaSE:(标准版)是java基础,早期叫j2se,2005改名叫JavaSE(必须). JavaME:(移动版)适合移动端的开发.j2me,2005改名叫java ME(不学) JavaEE:( ...

  6. Ironic几种不同的场景下的网络拓扑

    最近帮领导做了几页ppt,总结几种场景下ironic管理物理机网络的网络拓扑,简单做成一份文章记录下.只是方便自己记忆,没有认真修改.如果对ironic有一定了解,可以看下,加深理解. 1. vlan ...

  7. java:产生小数位数为2的随机概率,使得和为1

    public static List<InstSec> setDataSec(List<String> instno) { List<InstSec> result ...

  8. django框架 - 实时查看执行的sql语句

    django框架采用的ORM模型,我们可以通过mysql的日志记录实时看到执行的sql语句,具体步骤如下: 第一步:找到mysql的配置文件 第二步:编辑mysql配置文件 第三步:重启mysql 第 ...

  9. 判断ios或者android

    <script type="text/javascript"> $(function () { // android和iso下载链接 var u = navigator ...

  10. final、finally和finalize的区别

    final.finally和finalize的区别 这三者的区别可以从两个方面来说 1.意思解释方面 (1)final是修饰符(关键字) (2)finally是异常处理中的程序块 (3)finaliz ...