• 多线程编程学习笔记——线程池(二)


    接上文 多线程编程学习笔记——线程池(一)

    三、线程池与并行度

    此示例是学习如何应用线程池实现大量的操作,及与创建大量线程进行工作的区别。

    1. 代码如下

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Text;
    using System.Threading; 
    
    namespace ThreadPoolDemo
    {  
    
        class Program
        {   
    
            static void Main(string[] args)
            {
    
                Console.WriteLine("开始测试线程池与自创线程。。。");
                const int total = 500;
                long millisecondes = 0;
                Stopwatch sw = new Stopwatch();
                sw.Start();
                ThreadRun(total);
                sw.Stop();
                millisecondes = sw.ElapsedMilliseconds;
                decimal mom = (decimal)(Environment.WorkingSet / (1024 * 1024.0));            sw.Reset();
    
                sw.Start();
                ThreadPoolRun(total);
    
                sw.Stop();
                Console.WriteLine("自创线程总耗时 {0},占用内存:{1} MB", millisecondes,mom);
                Console.WriteLine("线程池总耗时 {0} ,占用内存:{1} MB", sw.ElapsedMilliseconds, Environment.WorkingSet / (1024 * 1024.0));
                Console.Read();
    
            }
    
            private static void  ThreadRun(int total)
            {
                using (var countdown = new CountdownEvent(total))
                {
                    Console.WriteLine("开始--自创线程。。。");
                    for (int i = 0; i < total; i++)
                    {
                        var t = new Thread(() =>
                        {
                            Console.WriteLine("自创线程ID :{0}", Thread.CurrentThread.ManagedThreadId);
                            Thread.Sleep(TimeSpan.FromSeconds(0.1));
                            countdown.Signal();//向 CountdownEvent 注册信号,同时减小 CurrentCount 的值。
    
                        });
                        t.Start();
                    }
                    countdown.Wait();  // 阻塞当前线程,直到 CountdownEvent 的信号数量变为 0
                    Console.WriteLine("-----------------");
                }
            }
    
            private static void ThreadPoolRun(int total)
            {
                using (var countdown = new CountdownEvent(total))
    
                {
                    Console.WriteLine("开始--线程池。。。");
                    for (int i = 0; i < total; i++)
                    {
                        ThreadPool.QueueUserWorkItem(_ =>
                        {
                            Console.WriteLine("线程池工作线程ID :{0}", Thread.CurrentThread.ManagedThreadId);
                            Thread.Sleep(TimeSpan.FromSeconds(0.1));
                            countdown.Signal();//向 CountdownEvent 注册信号,同时减小 CurrentCount 的值。
                        });                 
    
                    }
                    countdown.Wait();  // 阻塞当前线程,直到 CountdownEvent 的信号数量变为 0
                    Console.WriteLine("-----------------");
    
                }
            }
        }
    }
    
     

    2.程序运行结果如下图。

     

        1) 这个示例中我们自己创建了500个线程,每个线程一个操作,每个线程都阻塞100毫秒。总计耗时  11秒,消耗资源如下图。

     

        2)我们使用线程池执行相同的500个操作。总计耗时  9秒,消耗资源如下图。

     

    从1)与2)的比较上可以看出来,自创线程消耗的CPU资源比线程池要多。

    四、 从线程池中取消操作

    如果我们要从线程池中取消某个线程的操作,应该如何实现呢?本示例使用CancellationTokenSource和CancellationToken两个类来实现在取消线程池中的操作。这两个是是在net 4.0引入的。

     1.示例代码

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Text;
    using System.Threading; 
    
    namespace ThreadPoolDemo
    {
    
        class Program
        {    
    
            static void Main(string[] args)
            {
                Console.WriteLine("开始测试线程池中取消正在运行的线程。。。");
                using (var cts = new CancellationTokenSource())
                {
                    CancellationToken token = cts.Token;
                    ThreadPool.QueueUserWorkItem(_ => AsyncOper(token));
                    Thread.Sleep(TimeSpan.FromSeconds(2));
                    cts.Cancel();//取消线程操作
                }
    
                using (var cts = new CancellationTokenSource())
                {
                    CancellationToken token = cts.Token;
                    ThreadPool.QueueUserWorkItem(_ => AsyncOperation(token));
                    Thread.Sleep(TimeSpan.FromSeconds(2));
                    cts.Cancel();//取消线程操作
    
                }
                using (var cts = new CancellationTokenSource())
    
                {
                    CancellationToken token = cts.Token;
                    ThreadPool.QueueUserWorkItem(_ => AsyncOper3(token));
    
                    Thread.Sleep(TimeSpan.FromSeconds(2));
                    cts.Cancel();//取消线程操作
                }
    
                Thread.Sleep(TimeSpan.FromSeconds(2));
                Console.WriteLine("。。。。。。。。。。取消正在运行的线程结束。。。。。。");
                Console.Read();
            }
    
            private static void  AsyncOperation(CancellationToken token)
            {
    
                try
                {
                    Console.WriteLine("开始--线程池中的第二个工作线程。。。");
                    for (int i = 0; i < 5; i++)
    
                    {
                        token.ThrowIfCancellationRequested();//获取取消请求,抛出OperationCanceledException异常,
                        Thread.Sleep(TimeSpan.FromSeconds(1));
                    }
    
                    Console.WriteLine("-------线程池中的第二个工作线程 工作完成----------");
                }
                catch (OperationCanceledException ex)
                {
                    Console.WriteLine("使用抛出异常方法取消第二个工作线程  ID:{0},{1}", Thread.CurrentThread.ManagedThreadId,ex.Message);
                }
            }
            private static void AsyncOper(CancellationToken token)
            {
    
                Console.WriteLine("开始--线程池中的第一个工作线程。。。");
                for (int i = 0; i < 5; i++)
                {
                    if (token.IsCancellationRequested)//判断是否已经取消操作
                    {
                        Console.WriteLine("使用轮询方法取消工作线程  ID:{0}", Thread.CurrentThread.ManagedThreadId);
                        return;
                    }
                    Thread.Sleep(TimeSpan.FromSeconds(1));
                }
                Console.WriteLine("-------线程池中的第一个工作线程 工作完成----------");
            }
            private static void AsyncOper3(CancellationToken token)
            {
                Console.WriteLine("开始--线程池中的第三个工作线程。。。");
                bool cancel = false;
                token.Register(()=>cancel = true);
                for (int i = 0; i < 5; i++)
                {
                    if (cancel)//判断是否已经取消操作
                    {
                        Console.WriteLine("通过注册回调函数取消第三个工作线程  ID:{0}", Thread.CurrentThread.ManagedThreadId);
                        return;
                    }
                    Thread.Sleep(TimeSpan.FromSeconds(1));
                }   
    
                Console.WriteLine("-------线程池中的第三个工作线程 工作完成----------");
            }
        }
    }

    2.运行结果如下图。

     

    本示例一共实现了三种取消线程池中操作的方式。

    1. 轮询检查CancellationToken.IsCancellationRequested属性,如果为true,则说明操作被取消。
    2. 抛出一个OperationCancellationException异常。这允许操作之外的代码来取消操作。
    3. 注册一个回调函数,当操作取消时,线程池将调用回调函数,这样做的好处是将取消操作逻辑传递到另一个异步操作中。
  • 相关阅读:
    优秀的云架构师需要学什么技能
    dkh人力资源大数据解决方案整体架构
    大数据hadoop与spark的区别
    hadoop技术入门学习之发行版选择
    大数据开发基础知识需要掌握哪些
    智慧人社政务云平台建设方案架构案例介绍
    [项目机会]citrix 虚拟桌面对于java等高CPU占用率如何解决
    [办公自动化]无法使用江南天安usbkey 无法使用视频网站
    [学习笔记]从0到1
    [办公自动化]目录修改以及插入分页符后行间距自动变宽
  • 原文地址:https://www.cnblogs.com/chillsrc/p/7824936.html
Copyright © 2020-2023  润新知