[.Net线程处理系列]专题三:线程池中的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.

下面通过代码来学习下异步写入文件:


  
  
  1. using System;  
  2. using System.IO;  
  3. using System.Text;  
  4. using System.Threading;  
  5.  
  6. namespace AsyncFile  
  7. {  
  8.     class Program  
  9.     {  
  10.         static void Main(string[] args)  
  11.         {  
  12.             const int maxsize = 100000;  
  13.             ThreadPool.SetMaxThreads(1000,1000);  
  14.             PrintMessage("Main Thread start");  
  15.  
  16.             // 初始化FileStream对象  
  17.             FileStream filestream = new FileStream("test.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite, 100, true);  
  18.               
  19.             //打印文件流打开的方式  
  20.             Console.WriteLine("filestream is {0} opened Asynchronously", filestream.IsAsync ? "" : "not");  
  21.  
  22.             byte[] writebytes =new byte[maxsize];  
  23.             string writemessage = "An operation Use asynchronous method to write message.......................";  
  24.             writebytes = Encoding.Unicode.GetBytes(writemessage);  
  25.             Console.WriteLine("message size is: {0} byte\n", writebytes.Length);  
  26.             // 调用异步写入方法比信息写入到文件中  
  27.             filestream.BeginWrite(writebytes, 0, writebytes.Length, new AsyncCallback(EndWriteCallback), filestream);  
  28.             filestream.Flush();  
  29.             Console.Read();  
  30.  
  31.         }  
  32.  
  33.         // 当把数据写入文件完成后调用此方法来结束异步写操作  
  34.         private static void EndWriteCallback(IAsyncResult asyncResult)  
  35.         {  
  36.             Thread.Sleep(500);  
  37.             PrintMessage("Asynchronous Method start");  
  38.  
  39.             FileStream filestream = asyncResult.AsyncState as FileStream;  
  40.  
  41.             // 结束异步写入数据  
  42.             filestream.EndWrite(asyncResult);  
  43.             filestream.Close();  
  44.         }  
  45.  
  46.         // 打印线程池信息  
  47.         private static void PrintMessage(String data)  
  48.         {  
  49.             int workthreadnumber;  
  50.             int iothreadnumber;  
  51.  
  52.             // 获得线程池中可用的线程,把获得的可用工作者线程数量赋给workthreadnumber变量  
  53.             // 获得的可用I/O线程数量给iothreadnumber变量  
  54.             ThreadPool.GetAvailableThreads(out workthreadnumber, out iothreadnumber);  
  55.  
  56.             Console.WriteLine("{0}\n CurrentThreadId is {1}\n CurrentThread is background :{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is: {4}\n",  
  57.                 data,  
  58.                 Thread.CurrentThread.ManagedThreadId,  
  59.                 Thread.CurrentThread.IsBackground.ToString(),  
  60.                 workthreadnumber.ToString(),  
  61.                 iothreadnumber.ToString());  
  62.         }  
  63.     } 
运行结果:

从运行结果可以看出,此时是调用线程池中的I/O线程去执行回调函数的,同时在工程所的的bin\Debug文件目录下有生成一个text.txt文件,打开文件可以知道里面的内容正是你写入的。

下面演示如何从刚才的文件中异步读取我们写入的内容:


  
  
  1. using System;  
  2. using System.IO;  
  3. using System.Text;  
  4. using System.Threading;  
  5.  
  6. namespace AsyncFileRead  
  7. {  
  8.     class Program  
  9.     {  
  10.         const int maxsize = 1024;  
  11.         static byte[] readbytes = new byte[maxsize];  
  12.         static void Main(string[] args)  
  13.         {  
  14.             ThreadPool.SetMaxThreads(1000, 1000);  
  15.             PrintMessage("Main Thread start");  
  16.  
  17.             // 初始化FileStream对象  
  18.             FileStream filestream = new FileStream("test.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite, 100, false);  
  19.  
  20.             // 异步读取文件内容  
  21.             filestream.BeginRead(readbytes, 0, readbytes.Length, new AsyncCallback(EndReadCallback), filestream);  
  22.             Console.Read();  
  23.         }  
  24.  
  25.         private static void EndReadCallback(IAsyncResult asyncResult)  
  26.         {  
  27.             Thread.Sleep(1000);  
  28.             PrintMessage("Asynchronous Method start");  
  29.  
  30.             // 把AsyncResult.AsyncState转换为State对象  
  31.             FileStream readstream = (FileStream)asyncResult.AsyncState;  
  32.             int readlength = readstream.EndRead(asyncResult);  
  33.             if (readlength <=0)  
  34.             {  
  35.                 Console.WriteLine("Read error");  
  36.                 return;  
  37.             }  
  38.  
  39.             string readmessage = Encoding.Unicode.GetString(readbytes, 0, readlength);  
  40.             Console.WriteLine("Read Message is :" + readmessage);  
  41.             readstream.Close();  
  42.         }  
  43.  
  44.         // 打印线程池信息  
  45.         private static void PrintMessage(String data)  
  46.         {  
  47.             int workthreadnumber;  
  48.             int iothreadnumber;  
  49.  
  50.             // 获得线程池中可用的线程,把获得的可用工作者线程数量赋给workthreadnumber变量  
  51.             // 获得的可用I/O线程数量给iothreadnumber变量  
  52.             ThreadPool.GetAvailableThreads(out workthreadnumber, out iothreadnumber);  
  53.  
  54.             Console.WriteLine("{0}\n CurrentThreadId is {1}\n CurrentThread is background :{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is: {4}\n",  
  55.                 data,  
  56.                 Thread.CurrentThread.ManagedThreadId,  
  57.                 Thread.CurrentThread.IsBackground.ToString(),  
  58.                 workthreadnumber.ToString(),  
  59.                 iothreadnumber.ToString());  
  60.         }  
  61.     }  
运行结果:

这里有个需要注意的问题:如果大家测试的时候, 应该把开始生成的text.txt文件放到该工程下bin\debug\目录下, 我刚开始的做的时候就忘记拷过去的, 读出来的数据长度一直为0(这里我犯的错误写下了,希望大家可以注意,也是警惕自己要小心。)

二、I/O线程实现对请求的异步

我们同样可以利用I/O线程来模拟对浏览器对服务器请求的异步操作,在.net类库中的WebRequest类提供了异步请求的支持,

下面就来演示下如何实现请求异步:


  
  
  1. using System;  
  2. using System.Net;  
  3. using System.Threading;  
  4.  
  5. namespace RequestSample  
  6. {  
  7.     class Program  
  8.     {  
  9.         static void Main(string[] args)  
  10.         {  
  11.             ThreadPool.SetMaxThreads(1000, 1000);  
  12.             PrintMessage("Main Thread start");  
  13.  
  14.             // 发出一个异步Web请求  
  15.             WebRequest webrequest =WebRequest.Create("http://www.cnblogs.com/");  
  16.             webrequest.BeginGetResponse(ProcessWebResponse, webrequest);  
  17.  
  18.             Console.Read();  
  19.         }  
  20.  
  21.         // 回调方法  
  22.         private static void ProcessWebResponse(IAsyncResult result)  
  23.         {  
  24.             Thread.Sleep(500);  
  25.             PrintMessage("Asynchronous Method start");  
  26.  
  27.             WebRequest webrequest = (WebRequest)result.AsyncState;  
  28.             using (WebResponse webresponse = webrequest.EndGetResponse(result))  
  29.             {              
  30.                 Console.WriteLine("Content Length is : "+webresponse.ContentLength);  
  31.             }  
  32.         }  
  33.  
  34.         // 打印线程池信息  
  35.         private static void PrintMessage(String data)  
  36.         {  
  37.             int workthreadnumber;  
  38.             int iothreadnumber;  
  39.  
  40.             // 获得线程池中可用的线程,把获得的可用工作者线程数量赋给workthreadnumber变量  
  41.             // 获得的可用I/O线程数量给iothreadnumber变量  
  42.             ThreadPool.GetAvailableThreads(out workthreadnumber, out iothreadnumber);  
  43.  
  44.             Console.WriteLine("{0}\n CurrentThreadId is {1}\n CurrentThread is background :{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is: {4}\n",  
  45.                 data,  
  46.                 Thread.CurrentThread.ManagedThreadId,  
  47.                 Thread.CurrentThread.IsBackground.ToString(),  
  48.                 workthreadnumber.ToString(),  
  49.                 iothreadnumber.ToString());  
  50.         }  
  51.     }  
运行结果为:

三、小结 

    写到这里这篇关于I/O线程的文章也差不多写完了, 其实I/O线程还可以做很多事情,在网络(Socket)编程,web开发中都会用I/O线程,本来想写个Demo来展示多线程在实际的工作中都有那些应用的地方的, 但是后面觉得还是等多线程系列都讲完后再把知识一起串联起来做个Demo会好点,至于后面文章中将介绍下线程同步的问题。




     本文转自LearningHard 51CTO博客,原文链接:,http://blog.51cto.com/learninghard/1034790如需转载请自行联系原作者


相关文章
|
4月前
|
存储 监控 Java
Java多线程优化:提高线程池性能的技巧与实践
Java多线程优化:提高线程池性能的技巧与实践
137 1
|
1月前
|
监控 安全 Java
在 Java 中使用线程池监控以及动态调整线程池时需要注意什么?
【10月更文挑战第22天】在进行线程池的监控和动态调整时,要综合考虑多方面的因素,谨慎操作,以确保线程池能够高效、稳定地运行,满足业务的需求。
113 38
|
1月前
|
Java
.如何根据 CPU 核心数设计线程池线程数量
IO 密集型:核心数*2 计算密集型: 核心数+1 为什么加 1?即使当计算密集型的线程偶尔由于缺失故障或者其他原因而暂停时,这个额外的线程也能确保 CPU 的时钟周期不会被浪费。
47 4
|
1月前
|
开发框架 Java .NET
.net core 非阻塞的异步编程 及 线程调度过程
【11月更文挑战第12天】本文介绍了.NET Core中的非阻塞异步编程,包括其基本概念、实现方式及应用示例。通过`async`和`await`关键字,程序可在等待I/O操作时保持线程不被阻塞,提高性能。文章还详细说明了异步方法的基础示例、线程调度过程、延续任务机制、同步上下文的作用以及如何使用`Task.WhenAll`和`Task.WhenAny`处理多个异步任务的并发执行。
|
2月前
lua面向对象(类)和lua协同线程与协同函数、Lua文件I/O
Lua的面向对象编程、协同线程与协同函数的概念和使用,以及Lua文件I/O操作的基本方法。
39 4
lua面向对象(类)和lua协同线程与协同函数、Lua文件I/O
|
3月前
|
存储 缓存 Java
什么是线程池?从底层源码入手,深度解析线程池的工作原理
本文从底层源码入手,深度解析ThreadPoolExecutor底层源码,包括其核心字段、内部类和重要方法,另外对Executors工具类下的四种自带线程池源码进行解释。 阅读本文后,可以对线程池的工作原理、七大参数、生命周期、拒绝策略等内容拥有更深入的认识。
154 29
|
1月前
|
Java
线程池内部机制:线程的保活与回收策略
【10月更文挑战第24天】 线程池是现代并发编程中管理线程资源的一种高效机制。它不仅能够复用线程,减少创建和销毁线程的开销,还能有效控制并发线程的数量,提高系统资源的利用率。本文将深入探讨线程池中线程的保活和回收机制,帮助你更好地理解和使用线程池。
89 2
|
1月前
|
Prometheus 监控 Cloud Native
JAVA线程池监控以及动态调整线程池
【10月更文挑战第22天】在 Java 中,线程池的监控和动态调整是非常重要的,它可以帮助我们更好地管理系统资源,提高应用的性能和稳定性。
108 4
|
1月前
|
Prometheus 监控 Cloud Native
在 Java 中,如何使用线程池监控以及动态调整线程池?
【10月更文挑战第22天】线程池的监控和动态调整是一项重要的任务,需要我们结合具体的应用场景和需求,选择合适的方法和策略,以确保线程池始终处于最优状态,提高系统的性能和稳定性。
295 2
|
2月前
|
Dubbo Java 应用服务中间件
剖析Tomcat线程池与JDK线程池的区别和联系!
剖析Tomcat线程池与JDK线程池的区别和联系!
154 0
剖析Tomcat线程池与JDK线程池的区别和联系!