线程系列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

posted @ 2014-09-21 09:43  Darren Ji  阅读(1179)  评论(0编辑  收藏  举报

我的公众号:新语新世界,欢迎关注。