1.2 ~ 1.8 线程:创建、暂定、等待、终止、状态、优先级、前台线程和后台线程
1.2 使用C#创建线程
- 结果两组范围是1到10的数字会随机交叉输出。说明PrintNumber方法同时运行在主线程和另一个线程中。
- 在运行的程序是一个进程,一个进程是n个线程,始终有1个线程作为主线程。
Thread t = new Thread(支持ParameterizedThreadStart和ThreadStart委托);
,ParameterizedThreadStart用t.Start(参数)
执行(本方法只能传一个参数,如果想传多个参数,可以传集合),后者用t.Start()
方法执行。
static void Main(string[] args)
{
#region
Thread t = new Thread(Class1_1.PrintNumbers);
t.Start();
Class1_1.PrintNumbers();
Console.ReadLine();
#endregion
}
public class Class1_1
{
public static void PrintNumbers()
{
Console.WriteLine("staring...");
for (int i = 0; i < 10; i++)
{
Console.WriteLine(i);
}
}
}
1.3 暂定线程
- 让一个线程等待一段时间而不耗费操作系统资源。
Thread.Sleep(1000);
,线程暂停1s。
static void Main(string[] args)
{
Thread t = new Thread(printNumbrWithDelay);
t.Start();
PrintNumbers();
Console.ReadLine();
}
public static void printNumbrWithDelay()
{
Console.WriteLine("staring...");
for (int i = 0; i < 10; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(2));//等待2s,
Console.WriteLine(i);
}
}
public static void PrintNumbers()
{
Console.WriteLine("staring...");
for (int i = 0; i < 10; i++)
{
Console.WriteLine(i);
}
}
- 代码说明:因为t线程每次打印数字时会休眠2s(休眠期间,会占用尽可能少的CPU时间),PrintNumbers会先打印完。
1.4 线程等待
- 演示用
Join
方法让程序等待另一个线程执行完成。然后在代码中使用该线程的计算结果,用Thread.Sleep行不通,因为并不知道执行计算要花的具体时间。
static void Main(string[] args)
{
#region 1.4
Thread t = new Thread(Class1_4.PrintNumber);
t.Start();
t.Join();
Console.WriteLine("thread 结束");
Console.ReadLine();
#endregion
}
public class Class1_4
{
public static void PrintNumber()
{
Console.WriteLine("开始。。。");
for (int i = 0; i < 10; i++)
{
Thread.Sleep(500);
Console.WriteLine(i);
}
}
}
- 工作原理:当程序运行时,启动了一个耗时长的线程打印数字,但是主程序中调用了t.Join方法,该方法让我们等待直到线程t完成。主程序才会继续运行。借助该技术可以实现两个线程间同步执行步骤。第一个线程会等待另一个线程完成后再继续执行,类似于第一线程调Thread.Sleep方法让其处于阻塞状态,而t.Join方法好处是不用知道具体花费多长时间。
1.5 终止线程 t.Abort();
static void Main(string[] args)
{
#region 1.5
Thread t = new Thread(Class1_4.PrintNumber);
t.Start();
Thread.Sleep(3000);
t.Abort();
Console.WriteLine("线程{0}被终止了",Thread.CurrentThread.ManagedThreadId);
Class1_4.PrintNumber();
#endregion
}
- 工作原理:代码等待3s后对线程调用了
t.Abort()
是给线程t注入ThreadAbortException方法,导致线程终结。该异常很危险,因为可以在任何时候发生并且彻底摧毁应用程序,但是也不一定总能终结线程,目标线程可以通过处理该异常并调用Thread.ResetAbort
方法来拒绝被终止。不推荐用abort关闭线程,可优先用其他方法,如CancellationToken方法取消线程的执行。
1.6 检测线程状态
- 获得线程状态信息是非常有用的,因为线程是独立运行的,所以状态在任何时候都可以被改变。
Thread.CurrentThread.ThreadState
获得线程状态枚举值,具体枚举值可以F12进去看看。- 始终可以通过
Thread.CurrentThread
静态属性获得当前线程对象。
Thread t = new Thread(Class1_6.PrintNumber);
Console.WriteLine(t.ThreadState.ToString());
public static void PrintNumber()
{
Console.WriteLine("开始。。。");
Console.WriteLine(Thread.CurrentThread.ThreadState.ToString());
for (int i = 0; i < 10; i++)
{
Thread.Sleep(2000);
Console.WriteLine(i);
}
}
1.7 线程优先级
- 线程优先级决定了该线程可占用多少CPU时间。
t.Priority = ThreadPriority.Highest;//设置线程优先级
(最高优先级),Lowest(最低优先级)。在多核CPU中实际可能二者运行感觉没啥区别或很接近(单核时区别才会明显),但是如果有其他程序占用所有CPU核心,结果则会截然不同。设置Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1);
选项来模拟该情形,让操作系统将所有线程运行在单个CPU核心(第一个核心)上,得到结果是优先级高的得到运算时间多于优先级低的线程。
1.8 前台线程和后台线程
var t2 = new Thread(本处省略无参方法名);
t2.IsBackground = true;//设置为后台线程,不设置则该线程默认为前台线程
- 前台线程和后台线程区别:默认创建的线程是前台线程,设置IsBackGround为true则为后台线程,所有前台线程结束则程序结束,而此时后台线程还可以继续运行。