[.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,intbufferSize,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” 博客,请务必保留此出处http://learninghard.blog.51cto.com/6146675/1034790

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Java,使用线程池可以方便地开启多线程处理任务。Java提供了几种常见的线程池创建方式,其推荐使用ThreadPoolExecutor的构造器来创建适合业务场景的线程池。\[1\] 常见的线程池创建方式包括: 1. FixedThreadPool(固定线程池):线程池的大小一旦达到固定数量就会保持不变,适用于需要控制并发线程数量的场景。 2. SingleThreadExecutor(单线程化的线程池):只有一个线程线程池,任务按照提交的次序顺序执行,适用于需要按顺序执行任务的场景。 3. CachedThreadPool(可缓存线程池):线程池的大小可以根据需要自动调整,适用于需要处理大量短期任务的场景。 使用线程池的好处是可以提前创建好多个线程,放入线程池,使用时直接获取,使用完后放回池,避免频繁创建和销毁线程,实现线程的重复利用。线程池能够独立负责线程的创建、维护和分配,提高了线程的执行效率和资源利用率。\[2\]\[3\] #### 引用[.reference_title] - *1* *2* *3* [java多线程(线程池)使用总结](https://blog.csdn.net/domine/article/details/127342754)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值