线程系列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);
}}
使用Lambda表达式需要注意的一个问题。
class Program
{static void Main(string[] args){for (int i = 0; i < 10; i++){new Thread(() => Console.WriteLine("当前线程#" + Thread.CurrentThread.ManagedThreadId + "输出的值是:" + i)).Start();}}}
说明,有些线程共享了局部变量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();}}}
※ 使用线程的实例方法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;}}
以上,在主线程的catch语句块中,无法捕获另外一个线程的异常。
实际上,一个线程的异常必须在该线程方法内捕获。
class Program
{static void Main(string[] args){new Thread(Say).Start();
}static void Say(){try
{throw null;}catch (Exception)
{Console.WriteLine("捕获到异常了");
}}}
□ 线程池
当创建一个线程,会用几十到几百毫秒的时间创建该线程的栈,而且,在默认情况下,每个线程需要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,我已经是线程池中的线程了");
}}
使用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);
}}
※ 使用委托进入线程池
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;
}}
把方法赋值给委托变量,CLR会创建一个线程池中的线程。
线程系列包括:
线程系列01,前台线程,后台线程,线程同步
线程系列02,多个线程同时处理一个耗时较长的任务以节省时间
线程系列03,多线程共享数据,多线程不共享数据
线程系列04,传递数据给线程,线程命名,线程异常处理,线程池
线程系列05,手动结束线程
线程系列06,通过CLR代码查看线程池及其线程
线程系列07,使用lock语句块或Interlocked类型方法保证自增变量的数据同步
线程系列08,实现线程锁的各种方式,使用lock,Montor,Mutex,Semaphore以及线程死锁
线程系列10,无需显式调用线程的情形