# [C# 线程处理系列]专题三:线程池中的I/O线程
上一篇文章主要介绍了如何利用线程池中的工作者线程来实现多线程,使多个线程可以并发地工作,从而高效率地使用系统资源。在这篇文章中将介绍如何用线程池中的I/O线程来执行I/O操作,希望对大家有所帮助。
**目录:**
一、I/O线程实现对文件的异步
二、I/O线程实现对请求的异步
三、总结
**一、I/O线程实现对文件的异步**
**1.1 I/O线程介绍:**
对于线程所执行的任务来说,可以把线程分为两种类型:工作者线程和I/O线程。
工作者线程用来完成一些计算的任务,在任务执行的过程中,需要CPU不间断地处理,所以,在工作者线程的执行过程中,CPU和线程的资源是充分利用的。
I/O线程主要用来完成输入和输出的工作的,在这种情况下, 计算机需要I/O设备完成输入和输出的任务,在处理过程中,CPU是不需要参与处理过程的,此时正在运行的线程将处于等待状态,只有等任务完成后才会有事可做, 这样就造成线程资源浪费的问题。为了解决这样的问题,可以通过线程池来解决这样的问题,让线程池来管理线程,前面已经介绍过线程池了, 在这里就不讲了。
对于I/O线程,我们可以将输入输出操作分成三个步骤:启动、实际输入输出、处理结果。用于实际输入输出可由硬件完成,并不需要CPU的参与,而启动和处理结果也可以不在同一个线程上,这样就可以充分利用线程资源。在.Net中通过以Begin开头的方法来完成启动,以End开头的方法来处理结果,这两个方法可以运行在不同的线程,这样我们就实现了异步编程了。
**1.2 .Net中如何使用异步**
**注意:**
其实当我们调用Begin开头的方法就是将一个I/O线程排入到线程池中(调用Begin开头的方法就把I/O线程加入到线程池中管理都是.Net机制帮我们实现的)。
(因为有些人会问什么地方用到了线程池了,工作者线程由线程池管理很好看出来,因为创建工作者线程直接调用**ThreadPool.QueueUserWorkItem**方法来把工作者线程排入到线程池中)。
在.net Framework中的FCL中有许多类型能够对异步操作提供支持,其中在FileStream类中就提供了对文件的异步操作的方法。
FileStream类要调用I/O线程要实现异步操作,首先要建立一个FileStream对象。
通过下面的构造函数来初始化FileStream对象实现异步操作(异步读取和异步写入):
public FileStream (string path, FileMode mode, FileAccess access, FileShare share,int bufferSize,bool useAsync)
其中path代表文件的相对路径或绝对路径,mode代表如何打开或创建文件,access代表访问文件的方式,share代表文件如何由进程共享,buffersize代表缓冲区的大小,useAsync代表使用异步I/O还是同步I/O,设置为true时,说明使用异步I/O.
下面通过代码来学习下异步写入文件:
```
using System;
using System.IO;
using System.Text;
using System.Threading;
namespace AsyncFile
{
class Program
{
static void Main(string[] args)
{
const int maxsize = 100000;
ThreadPool.SetMaxThreads(1000,1000);
PrintMessage("Main Thread start");
// 初始化FileStream对象
FileStream filestream = new FileStream("test.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite, 100, true);
//打印文件流打开的方式
Console.WriteLine("filestream is {0} opened Asynchronously", filestream.IsAsync ? "" : "not");
byte[] writebytes =new byte[maxsize];
string writemessage = "An operation Use asynchronous method to write message.......................";
writebytes = Encoding.Unicode.GetBytes(writemessage);
Console.WriteLine("message size is: {0} byte\n", writebytes.Length);
// 调用异步写入方法比信息写入到文件中
filestream.BeginWrite(writebytes, 0, writebytes.Length, new AsyncCallback(EndWriteCallback), filestream);
filestream.Flush();
Console.Read();
}
// 当把数据写入文件完成后调用此方法来结束异步写操作
private static void EndWriteCallback(IAsyncResult asyncResult)
{
Thread.Sleep(500);
PrintMessage("Asynchronous Method start");
FileStream filestream = asyncResult.AsyncState as FileStream;
// 结束异步写入数据
filestream.EndWrite(asyncResult);
filestream.Close();
}
// 打印线程池信息
private static void PrintMessage(String data)
{
int workthreadnumber;
int iothreadnumber;
// 获得线程池中可用的线程,把获得的可用工作者线程数量赋给workthreadnumber变量
// 获得的可用I/O线程数量给iothreadnumber变量
ThreadPool.GetAvailableThreads(out workthreadnumber, out iothreadnumber);
Console.WriteLine("{0}\n CurrentThreadId is {1}\n CurrentThread is background :{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is: {4}\n",
data,
Thread.CurrentThread.ManagedThreadId,
Thread.CurrentThread.IsBackground.ToString(),
workthreadnumber.ToString(),
iothreadnumber.ToString());
}
}
}
```
运行结果:
![](https://box.kancloud.cn/2016-01-23_56a2eb32a2b37.png)
从运行结果可以看出,此时是调用线程池中的I/O线程去执行回调函数的,同时在工程所的的bin\Debug文件目录下有生成一个text.txt文件,打开文件可以知道里面的内容正是你写入的。
下面演示如何从刚才的文件中异步读取我们写入的内容:
```
using System;
using System.IO;
using System.Text;
using System.Threading;
namespace AsyncFileRead
{
class Program
{
const int maxsize = 1024;
static byte[] readbytes = new byte[maxsize];
static void Main(string[] args)
{
ThreadPool.SetMaxThreads(1000, 1000);
PrintMessage("Main Thread start");
// 初始化FileStream对象
FileStream filestream = new FileStream("test.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite, 100, false);
// 异步读取文件内容
filestream.BeginRead(readbytes, 0, readbytes.Length, new AsyncCallback(EndReadCallback), filestream);
Console.Read();
}
private static void EndReadCallback(IAsyncResult asyncResult)
{
Thread.Sleep(1000);
PrintMessage("Asynchronous Method start");
// 把AsyncResult.AsyncState转换为State对象
FileStream readstream = (FileStream)asyncResult.AsyncState;
int readlength = readstream.EndRead(asyncResult);
if (readlength <=0)
{
Console.WriteLine("Read error");
return;
}
string readmessage = Encoding.Unicode.GetString(readbytes, 0, readlength);
Console.WriteLine("Read Message is :" + readmessage);
readstream.Close();
}
// 打印线程池信息
private static void PrintMessage(String data)
{
int workthreadnumber;
int iothreadnumber;
// 获得线程池中可用的线程,把获得的可用工作者线程数量赋给workthreadnumber变量
// 获得的可用I/O线程数量给iothreadnumber变量
ThreadPool.GetAvailableThreads(out workthreadnumber, out iothreadnumber);
Console.WriteLine("{0}\n CurrentThreadId is {1}\n CurrentThread is background :{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is: {4}\n",
data,
Thread.CurrentThread.ManagedThreadId,
Thread.CurrentThread.IsBackground.ToString(),
workthreadnumber.ToString(),
iothreadnumber.ToString());
}
}
}
```
运行结果:
![](https://box.kancloud.cn/2016-01-23_56a2eb32b3230.png)
这里有个需要注意的问题:如果大家测试的时候, 应该把开始生成的text.txt文件放到该工程下bin\debug\目录下, 我刚开始的做的时候就忘记拷过去的, 读出来的数据长度一直为0(这里我犯的错误写下了,希望大家可以注意,也是警惕自己要小心。)
**二、I/O线程实现对请求的异步**
我们同样可以利用I/O线程来模拟对浏览器对服务器请求的异步操作,在.net类库中的WebRequest类提供了异步请求的支持,
下面就来演示下如何实现请求异步:
```
using System;
using System.Net;
using System.Threading;
namespace RequestSample
{
class Program
{
static void Main(string[] args)
{
ThreadPool.SetMaxThreads(1000, 1000);
PrintMessage("Main Thread start");
// 发出一个异步Web请求
WebRequest webrequest =WebRequest.Create("http://www.cnblogs.com/");
webrequest.BeginGetResponse(ProcessWebResponse, webrequest);
Console.Read();
}
// 回调方法
private static void ProcessWebResponse(IAsyncResult result)
{
Thread.Sleep(500);
PrintMessage("Asynchronous Method start");
WebRequest webrequest = (WebRequest)result.AsyncState;
using (WebResponse webresponse = webrequest.EndGetResponse(result))
{
Console.WriteLine("Content Length is : "+webresponse.ContentLength);
}
}
// 打印线程池信息
private static void PrintMessage(String data)
{
int workthreadnumber;
int iothreadnumber;
// 获得线程池中可用的线程,把获得的可用工作者线程数量赋给workthreadnumber变量
// 获得的可用I/O线程数量给iothreadnumber变量
ThreadPool.GetAvailableThreads(out workthreadnumber, out iothreadnumber);
Console.WriteLine("{0}\n CurrentThreadId is {1}\n CurrentThread is background :{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is: {4}\n",
data,
Thread.CurrentThread.ManagedThreadId,
Thread.CurrentThread.IsBackground.ToString(),
workthreadnumber.ToString(),
iothreadnumber.ToString());
}
}
}
```
运行结果为:
![](https://box.kancloud.cn/2016-01-23_56a2eb32c4af1.png)
写到这里这篇关于I/O线程的文章也差不多写完了, 其实I/O线程还可以做很多事情,在网络(Socket)编程,web开发中都会用I/O线程,本来想写个Demo来展示多线程在实际的工作中都有那些应用的地方的, 但是后面觉得还是等多线程系列都讲完后再把知识一起串联起来做个Demo会好点,至于后面文章中将介绍下线程同步的问题。
- C# 基础知识系列
- C# 基础知识系列 专题一:深入解析委托——C#中为什么要引入委托
- C# 基础知识系列 专题二:委托的本质论
- C# 基础知识系列 专题三:如何用委托包装多个方法——委托链
- C# 基础知识系列 专题四:事件揭秘
- C# 基础知识系列 专题五:当点击按钮时触发Click事件背后发生的事情
- C# 基础知识系列 专题六:泛型基础篇——为什么引入泛型
- C# 基础知识系列 专题七: 泛型深入理解(一)
- C# 基础知识系列 专题八: 深入理解泛型(二)
- C# 基础知识系列 专题九: 深入理解泛型可变性
- C#基础知识系列 专题十:全面解析可空类型
- C# 基础知识系列 专题十一:匿名方法解析
- C#基础知识系列 专题十二:迭代器
- C#基础知识 专题十三:全面解析对象集合初始化器、匿名类型和隐式类型
- C# 基础知识系列 专题十四:深入理解Lambda表达式
- C# 基础知识系列 专题十五:全面解析扩展方法
- C# 基础知识系列 专题十六:Linq介绍
- C#基础知识系列 专题十七:深入理解动态类型
- 你必须知道的异步编程 C# 5.0 新特性——Async和Await使异步编程更简单
- 全面解析C#中参数传递
- C#基础知识系列 全面解析C#中静态与非静态
- C# 基础知识系列 C#中易混淆的知识点
- C#进阶系列
- C#进阶系列 专题一:深入解析深拷贝和浅拷贝
- C#进阶系列 专题二:你知道Dictionary查找速度为什么快吗?
- C# 开发技巧系列
- C# 开发技巧系列 使用C#操作Word和Excel程序
- C# 开发技巧系列 使用C#操作幻灯片
- C# 开发技巧系列 如何动态设置屏幕分辨率
- C# 开发技巧系列 C#如何实现图片查看器
- C# 开发技巧 如何防止程序多次运行
- C# 开发技巧 实现属于自己的截图工具
- C# 开发技巧 如何使不符合要求的元素等于离它最近的一个元素
- C# 线程处理系列
- C# 线程处理系列 专题一:线程基础
- C# 线程处理系列 专题二:线程池中的工作者线程
- C# 线程处理系列 专题三:线程池中的I/O线程
- C# 线程处理系列 专题四:线程同步
- C# 线程处理系列 专题五:线程同步——事件构造
- C# 线程处理系列 专题六:线程同步——信号量和互斥体
- C# 多线程处理系列专题七——对多线程的补充
- C#网络编程系列
- C# 网络编程系列 专题一:网络协议简介
- C# 网络编程系列 专题二:HTTP协议详解
- C# 网络编程系列 专题三:自定义Web服务器
- C# 网络编程系列 专题四:自定义Web浏览器
- C# 网络编程系列 专题五:TCP编程
- C# 网络编程系列 专题六:UDP编程
- C# 网络编程系列 专题七:UDP编程补充——UDP广播程序的实现
- C# 网络编程系列 专题八:P2P编程
- C# 网络编程系列 专题九:实现类似QQ的即时通信程序
- C# 网络编程系列 专题十:实现简单的邮件收发器
- C# 网络编程系列 专题十一:实现一个基于FTP协议的程序——文件上传下载器
- C# 网络编程系列 专题十二:实现一个简单的FTP服务器
- C# 互操作性入门系列
- C# 互操作性入门系列(一):C#中互操作性介绍
- C# 互操作性入门系列(二):使用平台调用调用Win32 函数
- C# 互操作性入门系列(三):平台调用中的数据封送处理
- C# 互操作性入门系列(四):在C# 中调用COM组件
- CLR
- 谈谈: String 和StringBuilder区别和选择
- 谈谈:程序集加载和反射
- 利用反射获得委托和事件以及创建委托实例和添加事件处理程序
- 谈谈:.Net中的序列化和反序列化
- C#设计模式
- UML类图符号 各种关系说明以及举例
- C#设计模式(1)——单例模式
- C#设计模式(2)——简单工厂模式
- C#设计模式(3)——工厂方法模式
- C#设计模式(4)——抽象工厂模式
- C#设计模式(5)——建造者模式(Builder Pattern)
- C#设计模式(6)——原型模式(Prototype Pattern)
- C#设计模式(7)——适配器模式(Adapter Pattern)
- C#设计模式(8)——桥接模式(Bridge Pattern)
- C#设计模式(9)——装饰者模式(Decorator Pattern)
- C#设计模式(10)——组合模式(Composite Pattern)
- C#设计模式(11)——外观模式(Facade Pattern)
- C#设计模式(12)——享元模式(Flyweight Pattern)
- C#设计模式(13)——代理模式(Proxy Pattern)
- C#设计模式(14)——模板方法模式(Template Method)
- C#设计模式(15)——命令模式(Command Pattern)
- C#设计模式(16)——迭代器模式(Iterator Pattern)
- C#设计模式(17)——观察者模式(Observer Pattern)
- C#设计模式(18)——中介者模式(Mediator Pattern)
- C#设计模式(19)——状态者模式(State Pattern)
- C#设计模式(20)——策略者模式(Stragety Pattern)
- C#设计模式(21)——责任链模式
- C#设计模式(22)——访问者模式(Vistor Pattern)
- C#设计模式(23)——备忘录模式(Memento Pattern)
- C#设计模式总结
- WPF快速入门系列
- WPF快速入门系列(1)——WPF布局概览
- WPF快速入门系列(2)——深入解析依赖属性
- WPF快速入门系列(3)——深入解析WPF事件机制
- WPF快速入门系列(4)——深入解析WPF绑定
- WPF快速入门系列(5)——深入解析WPF命令
- WPF快速入门系列(6)——WPF资源和样式
- WPF快速入门系列(7)——深入解析WPF模板
- WPF快速入门系列(8)——MVVM快速入门
- WPF快速入门系列(9)——WPF任务管理工具实现
- ASP.NET 开发
- ASP.NET 开发必备知识点(1):如何让Asp.net网站运行在自定义的Web服务器上
- ASP.NET 开发必备知识点(2):那些年追过的ASP.NET权限管理
- ASP.NET中实现回调
- 跟我一起学WCF
- 跟我一起学WCF(1)——MSMQ消息队列
- 跟我一起学WCF(2)——利用.NET Remoting技术开发分布式应用
- 跟我一起学WCF(3)——利用Web Services开发分布式应用
- 跟我一起学WCF(3)——利用Web Services开发分布式应用
- 跟我一起学WCF(4)——第一个WCF程序
- 跟我一起学WCF(5)——深入解析服务契约 上篇
- 跟我一起学WCF(6)——深入解析服务契约 下篇
- 跟我一起学WCF(7)——WCF数据契约与序列化详解
- 跟我一起学WCF(8)——WCF中Session、实例管理详解
- 跟我一起学WCF(9)——WCF回调操作的实现
- 跟我一起学WCF(10)——WCF中事务处理
- 跟我一起学WCF(11)——WCF中队列服务详解
- 跟我一起学WCF(12)——WCF中Rest服务入门
- 跟我一起学WCF(13)——WCF系列总结
- .NET领域驱动设计实战系列
- .NET领域驱动设计实战系列 专题一:前期准备之EF CodeFirst
- .NET领域驱动设计实战系列 专题二:结合领域驱动设计的面向服务架构来搭建网上书店
- .NET领域驱动设计实战系列 专题三:前期准备之规约模式(Specification Pattern)
- .NET领域驱动设计实战系列 专题四:前期准备之工作单元模式(Unit Of Work)
- .NET领域驱动设计实战系列 专题五:网上书店规约模式、工作单元模式的引入以及购物车的实现
- .NET领域驱动设计实战系列 专题六:DDD实践案例:网上书店订单功能的实现
- .NET领域驱动设计实战系列 专题七:DDD实践案例:引入事件驱动与中间件机制来实现后台管理功能
- .NET领域驱动设计实战系列 专题八:DDD案例:网上书店分布式消息队列和分布式缓存的实现
- .NET领域驱动设计实战系列 专题九:DDD案例:网上书店AOP和站点地图的实现
- .NET领域驱动设计实战系列 专题十:DDD扩展内容:全面剖析CQRS模式实现
- .NET领域驱动设计实战系列 专题十一:.NET 领域驱动设计实战系列总结