• 线程系列04,传递数据给线程,线程命名,线程异常处理,线程池


    本篇体验:如何传递数据给线程,如何给线程命名,线程的异常处理,线程池。实在是太基础的部分。

     

    □ 传递数据给线程

    ※ 使用Lambda表达式

        class Program
    
        {
    
            static void Main(string[] args)
    
            {
    
                Thread t = new Thread(() => Say("hello", "world"));
    
                t.Start();
    
            }
    
            static void Say(string msg, string msg1)
    
            {
    
                Console.WriteLine("第一个参数值是:"+msg);
    
                Console.WriteLine("第二个参数值是:" + msg1);
    
            }
    
        }
    

    11

     

    使用Lambda表达式需要注意的一个问题。

        class Program
    
        {
    
            static void Main(string[] args)
    
            {
    
                for (int i = 0; i < 10; i++)
    
                {
    
                    new Thread(() => Console.WriteLine("当前线程#" + Thread.CurrentThread.ManagedThreadId + "输出的值是:" + i)).Start();
    
                }
    
            }
    
        }

    12

    说明,有些线程共享了局部变量i。如何解决线程共享栈变量的问题呢?只需要在每次遍历循环的时候,把i赋值给一个临时变量,再打印临时变量,不直接打印栈变量i。

        class Program
    
        {
    
            static void Main(string[] args)
    
            {
    
                for (int i = 0; i < 10; i++)
    
                {
    
                    int temp = i;
    
                    new Thread(() => Console.WriteLine("当前线程#" + Thread.CurrentThread.ManagedThreadId + "输出的值是:" + temp)).Start();
    
                }
    
            }
    
        }

    13
    打印的值没有重复的。


    ※ 使用线程的实例方法Start

        class Program
    
        {
    
            static void Main(string[] args)
    
            {
    
                Thread t = new Thread(Say);
    
                t.Start("hello");
    
            }
    
            static void Say(object msg)
    
            {
    
                string str = (string) msg;
    
                Console.WriteLine(str);
    
            }
    
        }
    

    之所以可以这样做,是因为Thread的构造函数可以接收2种类型的形参。

    public delegate void ThreadStart();
    
    public delegate void ParameterizedThreadStart (object obj);

    所有符合ParameterizedThreadStart这个委托定义的方法,必须有一个object类型的参数,然后在实例方法Start中传递参数。使用这种方式传递参数的弊端是:

    1、只能传递一个参数
    2、在方法体中,每次都要对object类型进行转换,拆箱

     

    □ 给线程命名

        class Program
    
        {
    
            static void Main(string[] args)
    
            {
    
                Thread.CurrentThread.Name = "主线程";
    
                Thread t = new Thread(Say);
    
                t.Name = "其它工作线程";
    
                t.Start();
    
                Say();
    
            }
    
            static void Say()
    
            {
    
                Console.WriteLine("我的名字是:" + Thread.CurrentThread.Name);
    
            }
    
        }
    

    通过Name属性可以设置或获取当前线程和实例线程的名称。

     

    □ 线程的异常处理

    一个线程无法捕获另外线程的异常。

        class Program
    
        {
    
            static void Main(string[] args)
    
            {
    
                try
    
                {
    
                    new Thread(Say).Start();
    
                }
    
                catch (Exception)
    
                {
    
                    Console.WriteLine("有异常");
    
                }
    
            }
    
            static void Say()
    
            {
    
                throw null;
    
            }
    
        }
    

    14
    以上,在主线程的catch语句块中,无法捕获另外一个线程的异常。

     

    实际上,一个线程的异常必须在该线程方法内捕获。

        class Program
    
        {
    
            static void Main(string[] args)
    
            {
    
                new Thread(Say).Start();
    
            }
    
            static void Say()
    
            {
    
                try
    
                {
    
                    throw null;
    
                }
    
                catch (Exception)
    
                {
    
                    Console.WriteLine("捕获到异常了");
    
                }
    
            }
    
        }
    

    15

     

    □ 线程池

    当创建一个线程,会用几十到几百毫秒的时间创建该线程的栈,而且,在默认情况下,每个线程需要1M的栈空间。线程池做到了共享和重复使用线程,性能得以提高。线程池还可以设置允许的最多线程数,一旦达到这个极限值,多余的线程需要排队,等待线程池中的线程执行结束再进入。

     

    线程池的默认的最大线程数因计算机不同而异。在.NET 4.0及其以后的版本中,在2位计算机最大线程数为1023个,64位为32768;在.NET3.5中,线程池最大线程数是250个;在.NET2.0中,线程池最大线程数为25个。当然,可以通过TreadPool.SetMaxThreads来设置线程池的最大线程数,也可以通过ThreadPool.SetMinThreads来设置最小线程数。

     

    线程池中线程的IsBackground属性默认为true。这意味着:主线程结束,应用程序结束,所有线程池中的后台线程结束。

     

    ※ 通过TPL进入线程池

    TPL,Task Parallel Library,是一个处理并行任务的一个类库。

        class Program
    
        {
    
            static void Main(string[] args)
    
            {
    
                Task tast = Task.Factory.StartNew(Say);
    
                tast.Wait();
    
            }
    
            static void Say()
    
            {
    
                Console.WriteLine("hello,我已经是线程池中的线程了");
    
            }
    
        }
    

    16

     

    使用System.Net.WebClient的实例方法DownloadString方法下载某个网页,使用泛型Task<T>来处理。

        class Program
    
        {
    
            static void Main(string[] args)
    
            {
    
                Task<string> task = Task.Factory.StartNew<string>(() => DownloadString("http://www.baidu.com"));
    
                Console.WriteLine("Task在工作呢,我玩会啊~~");
    
                Console.WriteLine("Task在工作呢,我玩会啊~~");
    
                Console.WriteLine("Task在工作呢,我玩会啊~~");
    
                string result = task.Result;
    
                Console.WriteLine(result);
    
            }
    
            static string DownloadString(string uri)
    
            {
    
                using (var wc = new System.Net.WebClient())
    
                {
    
                    return wc.DownloadString(uri);
    
                }
    
            }
    
        }
    

    ※ 不通过TPL进入线程池

        class Program
    
        {
    
            static void Main(string[] args)
    
            {
    
                ThreadPool.QueueUserWorkItem(Say);
    
                ThreadPool.QueueUserWorkItem(Say, 123);
    
                Console.ReadLine();
    
            }
    
            static void Say(object data)
    
            {
    
                Console.WriteLine("我来自线程池,我获取到的数据是:" + data);
    
            }
    
        }
    

    17  

     

    ※ 使用委托进入线程池

        class Program
    
        {
    
            static void Main(string[] args)
    
            {
    
                Func<string, int> method = GetLength;
    
                IAsyncResult ia = method.BeginInvoke("test", null, null);
    
                Console.WriteLine("线程池的线程在工作者呢,我先玩会~");
    
                int result = method.EndInvoke(ia);
    
                Console.WriteLine("test的长度为:" + result);
    
            }
    
            static int GetLength(string s)
    
            {
    
                return s.Length;
    
            }
    
        }
    

    18

    把方法赋值给委托变量,CLR会创建一个线程池中的线程。

    线程系列包括:

    线程系列01,前台线程,后台线程,线程同步

    线程系列02,多个线程同时处理一个耗时较长的任务以节省时间

    线程系列03,多线程共享数据,多线程不共享数据

    线程系列04,传递数据给线程,线程命名,线程异常处理,线程池

    线程系列05,手动结束线程

    线程系列06,通过CLR代码查看线程池及其线程

    线程系列07,使用lock语句块或Interlocked类型方法保证自增变量的数据同步

    线程系列08,实现线程锁的各种方式,使用lock,Montor,Mutex,Semaphore以及线程死锁

    线程系列09,线程的等待、通知,以及手动控制线程数量

    线程系列10,无需显式调用线程的情形


    参考资料:http://www.albahari.com/threading

  • 相关阅读:
    Ubuntu12.04安装svn1.8
    [NOIP模拟测试3] 建造游乐园 题解(欧拉图性质)
    图论模板
    [bzoj3073] Journeys 题解(线段树优化建图)
    [bzoj3033]太鼓达人 题解(搜索)
    [NOIP2016]天天爱跑步 题解(树上差分) (码长短跑的快)
    [HNOI2015]菜肴制作 题解(贪心+拓扑)
    [SDOI2015]排序 题解 (搜索)
    [CQOI2011]放棋子 题解(dp+组合数学)
    [ZJOI2011]看电影(组合数学/打表+高精)
  • 原文地址:https://www.cnblogs.com/darrenji/p/3984215.html
Copyright © 2020-2023  润新知