C# 线程相关知识总结

2020-06-17 15:24:00王振洲

多数情况下,可以不用new ThreadStart委托。直接在构造函数里传入void类型的方法。

Thread t = new Thread (Go); 

使用lambda表达式

static void Main()
{
 Thread t = new Thread ( () => Console.WriteLine ("Hello!") );
 t.Start();
}

Foreground线程和Background线程

默认情况下创建的线程都是Foreground,只要有一个Foregournd线程在执行,应用程序就不会关闭。
Background线程则不是。一旦Foreground线程执行完,应用程序结束,background就会强制结束。
可以用IsBackground来查看该线程是什么类型的线程。

线程异常捕获

public static void Main()
{
 try
 {
  new Thread (Go).Start();
 }
 catch (Exception ex)
 {
  // 不能捕获异常
  Console.WriteLine ("Exception!");
 }
}
 
static void Go() { throw null; }  //抛出 Null异常

此时并不能在Main方法里捕获线程Go方法的异常,如果是Thread自身的异常可以捕获。

正确捕获方式:

public static void Main()
{
  new Thread (Go).Start();
}
 
static void Go()
{
 try
 {
  // ...
  throw null;  // 这个异常会被下面捕获
  // ...
 }
 catch (Exception ex)
 {
   // ...
 }
}

线程池

当创建一个线程时,就会消耗几百毫秒cpu,创建一些新的私有局部变量栈。每个线程还消耗(默认)约1 MB的内存。线程池通过共享和回收线程,允许在不影响性能的情况下启用多线程。
每个.NET程序都有一个线程池,线程池维护着一定数量的工作线程,这些线程等待着执行分配下来的任务。

线程池线程注意点:

1 线程池的线程不能设置名字(导致线程调试困难)。

2 线程池的线程都是background线程

3 阻塞一个线程池的线程,会导致延迟。

4 可以随意设置线程池的优先级,在回到线程池时改线程就会被重置。
通过Thread.CurrentThread.IsThreadPoolThread.可以查看该线程是否是线程池的线程。

使用线程池创建线程的方法:

Task ThreadPool.QueueUserWorkItem Asynchronous delegates BackgroundWorker

TPL

Framework4.0下可以使用Task来创建线程池线程。调用Task.Factory.StartNew(),传递一个委托

Task.Factory.StartNew
static void Main() 
{
 Task.Factory.StartNew (Go);
}
 
static void Go()
{
 Console.WriteLine ("Hello from the thread pool!");
}

Task.Factory.StartNew 返回一个Task对象。可以调用该Task对象的Wait来等待该线程结束,调用Wait时会阻塞调用者的线程。

Task构造函数   给Task构造函数传递Action委托,或对应的方法,调用start方法,启动任务
static void Main() 
{
 Task t=new Task(Go);
 t.Start();
}
 
static void Go()
{
 Console.WriteLine ("Hello from the thread pool!");
}