C#线程学习笔记三:线程池中的I/O线程

本笔记摘抄自:https://www.cnblogs.com/zhili/archive/2012/07/20/MultiThreads.html,记录一下学习过程以备后续查用。

    一、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线程排入到线程池中(由.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。

下面代码演示异步写入文件:

class Program
    {
        static void Main(string[] args)
        {
            #region I/O线程:异步写入文件
            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 with 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 sizes is:{0} bytes.\n", writeBytes.Length);
            //调用异步写入方法将信息写入到文件中
            fileStream.BeginWrite(writeBytes, 0, writeBytes.Length, new AsyncCallback(EndWriteCallback), fileStream);
            fileStream.Flush();
            Console.Read();
            #endregion
        }

        /// <summary>
        /// 打印线程池信息
        /// </summary>
        /// <param name="data"></param>
        private static void PrintMessage(string data)
        {
            //获得线程池中可用的工作者线程数量及I/O线程数量
            ThreadPool.GetAvailableThreads(out int workThreadNumber, out int 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());
        }

        /// <summary>
        /// 当数据写入文件完成后调用此方法来结束异步写操作
        /// </summary>
        /// <param name="asyncResult"></param>
        private static void EndWriteCallback(IAsyncResult asyncResult)
        {
            Thread.Sleep(500);
            PrintMessage("Asynchronous method start.");

            FileStream filestream = asyncResult.AsyncState as FileStream;

            //结束异步写入数据
            filestream.EndWrite(asyncResult);
            filestream.Close();
        }
    }

运行结果如下:

从运行结果可以看出,此时是调用线程池中的I/O线程去执行回调函数的,同时在项目的bin\Debug文件目录下生成了一个Test.txt文件。

下面代码演示异步读取文件:

class Program
    {
        //异步读取文件
        const int maxSize = 1024;
        private static readonly byte[] readBytes = new byte[maxSize];

        static void Main(string[] args)
        {
            #region I/O线程:异步读取文件
            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();
            #endregion
        }

        /// <summary>
        /// 打印线程池信息
        /// </summary>
        /// <param name="data"></param>
        private static void PrintMessage(string data)
        {
            //获得线程池中可用的工作者线程数量及I/O线程数量
            ThreadPool.GetAvailableThreads(out int workThreadNumber, out int 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());
        }

        /// <summary>
        /// 当数据读取文件完成后调用此方法来结束异步写操作
        /// </summary>
        /// <param name="asyncResult"></param>
        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();
        }
    }

运行结果如下:

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

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

下面代码演示异步请求:

class Program
    {
        static void Main(string[] args)
        {
            #region I/O线程:异步请求
            ThreadPool.SetMaxThreads(1000, 1000);
            PrintMessage("Main thread start.");

            // 发出一个异步Web请求
            WebRequest webrequest = WebRequest.Create("https://www.cnblogs.com/");
            webrequest.BeginGetResponse(ProcessWebResponse, webrequest);

            Console.Read();
            #endregion
        }

        /// <summary>
        /// 打印线程池信息
        /// </summary>
        /// <param name="data"></param>
        private static void PrintMessage(string data)
        {
            //获得线程池中可用的工作者线程数量及I/O线程数量
            ThreadPool.GetAvailableThreads(out int workThreadNumber, out int 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());
        }

        /// <summary>
        /// Web请求回调函数
        /// </summary>
        /// <param name="result"></param>
        private static void ProcessWebResponse(IAsyncResult result)
        {
            Thread.Sleep(500);
            PrintMessage("Asynchronous method start.");

            WebRequest webRequest = (WebRequest)result.AsyncState;
            using (WebResponse wr = webRequest.EndGetResponse(result))
            {
                Console.WriteLine("Content length is : " + wr.ContentLength);
            }
        }
    }

运行结果如下:

(0)

相关推荐

  • C# Thread IsBackground 前后台线程

    Thread 区别前后台线程属性IsBackground 1.  创建一个线程默认是前台线程,即IsBackground=true 2.  主线程的结束会关联前台线程,前台线程会阻止主进程的结束,需等 ...

  • 多线程之旅(Thread)

    在上篇文章中我们已经知道了多线程是什么了,那么它到底可以干嘛呢?这里特别声明一个前面的委托没看的同学可以到上上上篇博文查看,因为多线程要经常使用到委托.源码一.异步.同步1.同步(在计算的理解总是要你 ...

  • C#线程学习笔记六:线程同步--信号量和互斥体

    本笔记摘抄自:https://www.cnblogs.com/zhili/archive/2012/07/23/Mutex_And_Semaphore.html,记录一下学习过程以备后续查用.     ...

  • C#线程学习笔记七:Task详细用法

    一.Task类简介: Task类是在.NET Framework 4.0中提供的新功能,主要用于异步操作的控制.它比Thread和ThreadPool提供了更为强大的功能,并且更方便使用. Task和 ...

  • C#线程学习笔记八:async & await入门一

    一.涉及内容 async & await是C# 5.0引入的,控制台输出所使用的$符号(拼接字符串)是C# 6.0引入的,其功能类似于string.Format()方法. 二.多线程.异步.同 ...

  • Python爬虫学习笔记(三)

    Cookies: 以抓取https://www.yaozh.com/为例 Test1(不使用cookies): 代码: import urllib.request# 1.添加URLurl = &quo ...

  • C# LINQ学习笔记三:LINQ to OBJECT之操作字符串

    一.统计单词在字符串中出现的次数 请注意,若要执行计数,请先调用Split方法来创建词数组.Split方法存在性能开销,如果对字符串执行的唯一操作是计数词,则应考虑改用Matches或 IndexOf ...

  • 【学习笔记】houdini sop中制作彩色线条/毛发模拟

    --  微资讯 · 微课程  -- 利用零碎时间,走上超神之路! 小编导语 非常简单的另类的制作毛发效果的教程,本来作为预览工具的volumetrail工具还可以直接用来制作造型渲染,学习如何能够渲染 ...

  • 《中医基础理论》学习笔记(三)

    气一元论 中国古代哲学的物质观,从五行的多元论到阴阳二气的二元论,最终统一于气的一元论."太极一气产阴阳,阴阳化合生五行,五行既萌,遂含万物".天地万物皆本于气,人亦因气而生.气是 ...

  • 学习笔记:在秩序中追求自由,在规则中追求平等、在尊重中培养德行

    先做两个俗套的选择题 问题一:老婆和老妈一起掉河里,一次只能救一个,你救谁? 问题二:女朋友和老妈一起掉河里,一次只能救一个,你救谁? 2021年4月,法学专家.网红学者罗翔到混沌大学做了题为< ...

  • 古籍学习笔记:十天干中的人生秘密

    天干相配:甲:丁.癸.庚乙:癸.丙.干土或湿土,忌金丙:壬.木,忌土丁:甲.庚,水多用戊戌戊:丙.癸.甲已:夏癸.冬丙,忌壬.甲庚:丁.甲.壬辛:已.壬.丙,土多用甲,忌丁壬:癸.金癸:庚.辛.壬,水 ...