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

多线程与异步编程的异同

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. 正则表达式的方法:replace,match,test(replace参数可以是回调函数)

    1.replace: 作用对象:字符串 功能:用于替换字符串中的某些字符 参数:(1)正则表达式 (2)要替换的字符串 或者 回调函数 var str="1 2 3 4 5 6 7 8 9& ...

  2. 使用wireshark抓取TCP包分析1

    使用wireshark抓取TCP包分析1 前言 介绍 目的 准备工作 传输 创建连接 握手 生成密钥 发送数据 断开连接 结论 前言 介绍 本篇文章是使用wireshrak对某个https请求的tcp ...

  3. Node.js入门(含NVM、NPM、NVM的安装)

    本文最初发表于博客园,并在GitHub上持续更新前端的系列文章.欢迎在GitHub上关注我,一起入门和进阶前端. 以下是正文. Node.js的介绍 引擎 引擎的特性: JS的内核即引擎.因为引擎有以 ...

  4. Java中子类能继承父类的私有属性吗?

    前段时间去听老师讲课的时候,老师告诉我子类是可以继承父类所有的属性和方法的.当时我是极其疑惑的,因为之前学校考试时这个考点我记得很清楚:子类只能继承父类的非私有属性和方法.老师给我的解释是这样的--先 ...

  5. 运行web项目端口占用问题

    ---恢复内容开始--- 有时候运行web项目会提示8080端口已经被占用这一类问题(Error running Tomcat8: Address localhost:1099 is already ...

  6. JavaScript基础教程2-20160612

    1.JavaScript之操作html元素,Dom Dom是抽象出来的网页对象,需要了解面向对象的思想:调用对象下的方法实现相应的功能 使用JS调用dom来创建标签. //document是网页对象 ...

  7. AES对称加密

    import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.Secre ...

  8. mysql字符设置

    MySQL字符集设置 mysql>CREATE DATABASE IF NOT EXISTS mydb default charset utf8 COLLATE utf8_general_ci; ...

  9. CEPH RGW集群和bucket的zone group 不一致导致的404异常解决 及 使用radosgw-admin metadata 命令设置bucket metadata 的方法

      问题现象: 最近在研究zonegroup的配置操作,发现在配置zonegroup后修改了default zone,导致访问对象报404错误. 问题原因: rgw 日志 报异常'request fo ...

  10. mysql打不开表问题解决方案

    做开发时候某一表怎么也打不开,数据也不多,网上查了按下面这篇文章完美解决,但是要记得用root登录mysql: 记一次MySQL中Waiting for table metadata lock的解决方 ...