zoukankan      html  css  js  c++  java
  • C#线程学习笔记二:线程池中的工作者线程

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

        一、线程池基础

        首先,创建和销毁线程是一个要耗费大量时间的过程,其次,太多的线程也会浪费内存资源,所以通过Thread类来创建过多的线程反而有损于性能。为了改善这样

    的问题 ,.NET中就引入了线程池。

        线程池形象的表示就是存放应用程序中使用的线程的一个集合(就是放线程的地方,这样线程都放在一个地方就好管理了)。

        CLR初始化时,线程池中是没有线程的,在内部, 线程池维护了一个操作请求队列。当应用程序想执行一个异步操作时,就调用一个方法,将一个任务放到线程池

    的队列中,线程池代码从队列中提取任务,将这个任务委派给一个线程池线程去执行,当线程池线程完成任务时,线程不会被销毁,而是返回到线程池中,等待响应另

    一个请求。由于线程不被销毁, 这样就可以避免因为创建线程所产生的性能损失。

        MSDN表述:

        “线程池经常用在服务器应用程序中,每一个新进来的需求被分配给一个线程池中的线程,这样该需求能被异步的执行,没有阻碍主线程或推迟后继需求的处理。”

        注意:通过线程池创建的线程默认为后台线程,优先级默认为Normal。

        二、通过线程池的工作者线程实现异步

        2.1创建工作者线程的方法

        public static bool QueueUserWorkItem (WaitCallback callback);

        public static bool QueueUserWorkItem(WaitCallback callback, Object state);

        这两个方法向线程池的队列添加一个工作项(work item)以及一个可选的状态数据,然后,这两个方法就会立即返回。

        工作项其实就是由callback参数标识的一个方法,该方法将由线程池线程执行。同时写的回调方法必须匹配System.Threading.WaitCallback委托类型,定义为:

        public delegate void WaitCallback(Object state);

        下面演示如何通过线程池线程来实现异步调用:

        class Program
        {
            static void Main(string[] args)
            {
                #region 通过线程池的工作者线程实现异步
                //设置线程池中工作者线程最大数量为1000,I/O线程最大数量为1000。
                ThreadPool.SetMaxThreads(1000, 1000);
                Console.WriteLine("Main thread: queue an asynchronous method.");
                PrintMessage("Main thread start.");
    
                //把工作项添加到队列中,此时线程池会用工作者线程去执行回调方法。
                ThreadPool.QueueUserWorkItem(AsyncMethod);
                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}
     CurrentThreadId is:{1}
     CurrentThread is background:{2}
     WorkerThreadNumber is:{3}
     IOThreadNumbers is:{4}
    ",
                    data,
                    Thread.CurrentThread.ManagedThreadId,
                    Thread.CurrentThread.IsBackground.ToString(),
                    workThreadNumber.ToString(),
                    ioThreadNumber.ToString());
            }
    
            /// <summary>
            /// 异步方法:必须匹配WaitCallback委托
            /// </summary>
            /// <param name="state"></param>
            private static void AsyncMethod(object state)
            {
                Thread.Sleep(1000);
                PrintMessage("Asynchoronous method.");
                Console.WriteLine("Asynchoronous thread has worked.");
            }
        }

        运行结果如下:

        从结果中可以看出,线程池中的可用的工作者线程少了一个,用去执行回调方法了。

        ThreadPool.QueueUserWorkItem(WaitCallback callback,Object state) 方法可以把object对象作为参数传送到回调函数中,使用方法与

    ThreadPool.QueueUserWorkItem(WaitCallback callback)类似,这里就不列出了。

        2.2 协作式取消

        .NET Framework提供了取消操作的模式, 这个模式是协作式的。为了取消一个操作,首先必须创建一个System.Threading.CancellationTokenSource对象。

        下面代码演示协作式取消的使用,主要实现当用户在控制台敲下回车键后就停止数数方法。

        class Program
        {
            static void Main(string[] args)
            {
                #region 协作式取消
                ThreadPool.SetMaxThreads(1000, 1000);
                Console.WriteLine("Main thread run.");
                PrintMessage("Start");
                Run();
                Console.ReadKey();
                #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}
     CurrentThreadId is:{1}
     CurrentThread is background:{2}
     WorkerThreadNumber is:{3}
     IOThreadNumbers is:{4}
    ",
                    data,
                    Thread.CurrentThread.ManagedThreadId,
                    Thread.CurrentThread.IsBackground.ToString(),
                    workThreadNumber.ToString(),
                    ioThreadNumber.ToString());
            }
    
            /// <summary>
            /// 运行工作者线程(包含协作式取消)
            /// </summary>
            private static void Run()
            {
                CancellationTokenSource cts = new CancellationTokenSource();
    
                //这里是用Lambda表达式的写法,效果一样。
                //ThreadPool.QueueUserWorkItem(obj => Count(cts.Token, 1000));
    
                ThreadPool.QueueUserWorkItem(Callback, cts.Token);
                Console.WriteLine("Press enter key to cancel the operation.
    ");
                Console.ReadLine();
                //传达取消请求
                cts.Cancel();
            }
    
            /// <summary>
            /// 回调函数
            /// </summary>
            /// <param name="state"></param>
            private static void Callback(object state)
            {
                Thread.Sleep(1000);
                PrintMessage("Asynchoronous method start.");
                CancellationToken token = (CancellationToken)state;
                Count(token, 1000);
            }
    
            /// <summary>
            /// 数数
            /// </summary>
            /// <param name="token"></param>
            /// <param name="countTo"></param>
            private static void Count(CancellationToken token, int countTo)
            {
                for (int i = 1; i <= countTo; i++)
                {
                    if (token.IsCancellationRequested)
                    {
                        Console.WriteLine("Count is canceled.");
                        break;
                    }
    
                    Console.WriteLine(i);
                    Thread.Sleep(300);
                }
                Console.WriteLine("Count has done.");
            }
        }

        运行结果如下:

        三、使用委托实现异步

        涉及术语解释--异步编程模型:

        APM 异步编程模型(Asynchronous Programming Model)

        EAP 基于事件的异步编程模式(Event-based Asynchronous Pattern)

        TAP 基于任务的异步编程模式(Task-based Asynchronous Pattern)

        通过调用ThreadPool的QueueUserWorkItem方法来来启动工作者线程非常方便,但委托WaitCallback指向的是带有一个参数的无返回值的方法。如果我们实际操作中

    需要有返回值,或者需要带有多个参数, 这时通过这样的方式就难以实现了。 为了解决这样的问题,我们可以通过委托来建立工作这线程。

        下面代码演示使用委托实现异步:

        class Program
        {
            //使用委托实现异步,是使用了异步编程模型APM。
            private delegate string ThreadDelegate();
    
            static void Main(string[] args)
            {
                #region 使用委托实现异步
                ThreadPool.SetMaxThreads(1000, 1000);
                PrintMessage("Main thread start.");
    
                //实例化委托
                ThreadDelegate threadDelegate = new ThreadDelegate(AsyncMethod);
                //异步调用委托
                IAsyncResult result = threadDelegate.BeginInvoke(null, null);
                //获取结果并打印
                string returnData = threadDelegate.EndInvoke(result);
                Console.WriteLine(returnData);
                Console.ReadLine();
                #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}
     CurrentThreadId is:{1}
     CurrentThread is background:{2}
     WorkerThreadNumber is:{3}
     IOThreadNumbers is:{4}
    ",
                    data,
                    Thread.CurrentThread.ManagedThreadId,
                    Thread.CurrentThread.IsBackground.ToString(),
                    workThreadNumber.ToString(),
                    ioThreadNumber.ToString());
            }
    
            /// <summary>
            /// 异步方法
            /// </summary>
            /// <returns></returns>
            private static string AsyncMethod()
            {
                Thread.Sleep(1000);
                PrintMessage("Asynchoronous method.");
                return "Method has completed.";
            }
        }

        运行结果如下:

        四、任务

        同样,任务的引入也是为了解决通过ThreadPool.QueueUserWorkItem中限制的问题。

        4.1 使用任务来实现异步

        class Program
        {
            static void Main(string[] args)
            {
                #region 使用任务实现异步
                ThreadPool.SetMaxThreads(1000, 1000);
                PrintMessage("Main thread start.");
                //调用构造函数创建Task对象
                Task<int> task = new Task<int>(n => AsyncMethod((int)n), 10);
    
                //启动任务 
                task.Start();
                //等待任务完成
                task.Wait();
                Console.WriteLine("The method result is: " + task.Result);
                Console.ReadLine();
                #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}
     CurrentThreadId is:{1}
     CurrentThread is background:{2}
     WorkerThreadNumber is:{3}
     IOThreadNumbers is:{4}
    ",
                    data,
                    Thread.CurrentThread.ManagedThreadId,
                    Thread.CurrentThread.IsBackground.ToString(),
                    workThreadNumber.ToString(),
                    ioThreadNumber.ToString());
            }
    
            /// <summary>
            /// 异步方法
            /// </summary>
            /// <param name="n"></param>
            /// <returns></returns>
            private static int AsyncMethod(int n)
            {
                Thread.Sleep(1000);
                PrintMessage("Asynchoronous method.");
    
                int sum = 0;
                for (int i = 1; i < n; i++)
                {
                    //运算溢出检查
                    checked
                    {
                        sum += i;
                    }
                }
    
                return sum;
            }
        }

        运行结果如下:

        4.2 取消任务

        如果要取消任务, 同样也可以CancellationTokenSource对象来取消。

        下面代码演示取消一个任务:

        class Program
        {
            static void Main(string[] args)
            {
                #region 取消任务
                ThreadPool.SetMaxThreads(1000, 1000);
                PrintMessage("Main thread start.");
                CancellationTokenSource cts = new CancellationTokenSource();
    
                //调用构造函数创建Task对象,将一个CancellationToken传给Task构造器从而使Task和CancellationToken关联起来。
                Task<int> task = new Task<int>(n => AsyncMethod(cts.Token, (int)n), 10);
    
                //启动任务 
                task.Start();
                //延迟取消任务
                Thread.Sleep(3000);
    
                //取消任务
                cts.Cancel();
                Console.WriteLine("The method result is: " + task.Result);
                Console.ReadLine();
                #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}
     CurrentThreadId is:{1}
     CurrentThread is background:{2}
     WorkerThreadNumber is:{3}
     IOThreadNumbers is:{4}
    ",
                    data,
                    Thread.CurrentThread.ManagedThreadId,
                    Thread.CurrentThread.IsBackground.ToString(),
                    workThreadNumber.ToString(),
                    ioThreadNumber.ToString());
            }
    
            /// <summary>
            /// 异步方法
            /// </summary>
            /// <param name="ct"></param>
            /// <param name="n"></param>
            /// <returns></returns>
            private static int AsyncMethod(CancellationToken ct, int n)
            {
                Thread.Sleep(1000);
                PrintMessage("Asynchoronous method.");
    
                int sum = 0;
                try
                {
                    for (int i = 1; i < n; i++)
                    {
                        //当CancellationTokenSource对象调用Cancel方法时,就会引起OperationCanceledException异常,
                        //通过调用CancellationToken的ThrowIfCancellationRequested方法来定时检查操作是否已经取消,
                        //这个方法和CancellationToken的IsCancellationRequested属性类似。
                        ct.ThrowIfCancellationRequested();
                        Thread.Sleep(500);
                        //运算溢出检查
                        checked
                        {
                            sum += i;
                        }
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine("Exception is:" + e.GetType().Name);
                    Console.WriteLine("Operation is canceled.");
                }
    
                return sum;
            }
        }

        运算结果如下:

        4.3 使用任务工厂实现异步操作

        同样也可以通过任务工厂TaskFactory类型来实现异步操作。

        class Program
        {
            static void Main(string[] args)
            {
                #region 使用任务工厂实现异步
                ThreadPool.SetMaxThreads(1000, 1000);
                Task.Factory.StartNew(() => PrintMessage("Main thread."));
                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}
     CurrentThreadId is:{1}
     CurrentThread is background:{2}
     WorkerThreadNumber is:{3}
     IOThreadNumbers is:{4}
    ",
                    data,
                    Thread.CurrentThread.ManagedThreadId,
                    Thread.CurrentThread.IsBackground.ToString(),
                    workThreadNumber.ToString(),
                    ioThreadNumber.ToString());
            }
        }

        运行结果如下:

     

  • 相关阅读:
    数据库的左右表连接
    数据库索引-索引的优点、缺点、分类、不走索引的情况
    selenium中日期控件的操作方法
    esdoc 自动生成接口文档介绍
    css设置背景图(背景图太大导致图片显示不全)
    k8s调度器及调度队列源码分析
    MIT6.824 2020 Lab1 MapReduce 实现
    git命令
    python3.6、3.9多版本安装共存
    使用蓝鲸平台登录态验证接入第三方系统
  • 原文地址:https://www.cnblogs.com/atomy/p/11946785.html
Copyright © 2011-2022 走看看