1. 线程的基本概念
简单的讲进程就是程序分配在内存当中,等待处理器进行处理,请记住线程会消耗大量的操作系统资源。多个线程共享一个物理处理器将导致处理器忙于处理管理这些进程,而无法运行程序。
使用线程通常是一个操作系统的任务,试图在一个单核CPU上并行执行计算任务是没有任何意义的,可能比顺序执行花费的时间更长。
为了更好的利用现代处理器的能力,使用多线程处理程序发挥出最好的作用,这需要组织多个线程间的通讯和相互同步。
下面将学习一下 线程的生命周期,和创建线程、挂起线程、线程等待、以及终止线程。
创建一个线程操作
static void Main(string[] args) { //创建一个新的线程来实现输出数字 Thread t = new Thread(PrintNumber); t.Start(); //这一行代码是在创建了一个新的线程并行执行的 PrintNumber(); Console.ReadLine(); } static void PrintNumber() { Console.WriteLine("Starting..."); for (int i = 0; i < 10; i++) { Console.WriteLine(i); } }
从上面代码可以看出一个程序始终有一个主线程在执行,而Thread是创建一个新的线程执行。两者之间是同步执行的。
暂停线程
static void Main(string[] args) { //创建一个带暂停的进程 Thread t = new Thread(PrintNumberWithdelay); t.Start(); //这一行就是始终执行的主线程经过(一个程序都有一个始终执行的主线程) PrintNumber(); } static void PrintNumber() { Console.WriteLine("Starting..."); for (int i = 0; i < 10; i++) { Console.WriteLine(i); } } static void PrintNumberWithdelay() { Console.WriteLine("暂停..."); for (int i = 0; i < 10; i++) { Thread.Sleep(TimeSpan.FromSeconds(2)); Console.WriteLine(i); } }
很明显,主线程已经执行完毕,而新的线程在输出暂停以后还在继续执行。每次执行都会休眠2秒钟。
线程等待
那么如何不让主线程继续往下执行,而是等待新线程执行完毕再往下执行呢。
static void Main(string[] args) { //创建一个带暂停的进程 Thread t = new Thread(PrintNumberWithdelay); t.Start(); t.Join();//线程等待 Console.WriteLine("执行完毕了"); } static void PrintNumberWithdelay() { Console.WriteLine("暂停..."); for (int i = 0; i < 10; i++) { Thread.Sleep(TimeSpan.FromSeconds(1)); Console.WriteLine(i); } }
终止线程
static void Main(string[] args) { //创建一个带暂停的进程 Thread t = new Thread(PrintNumberWithdelay); t.Start(); Thread.Sleep(TimeSpan.FromSeconds(3)); t.Abort();//终止线程 Console.WriteLine("线程终止了"); }
这段代码是给线程注入了ThreadAbortException方法,导致线程被终结,这样操作是非常危险的,因为该异常可能会导致整个应用程序都崩溃。
可以使用 ResetAbort 方法来拒绝被终止。
检测线程状态
static void Main(string[] args) { //创建一个带暂停的进程 Thread t = new Thread(PrintNumberWithdelay); Thread t1 = new Thread(DoNothing); t1.Start(); t.Start(); for (int i = 0; i < 30; i++) { Console.WriteLine(t.ThreadState.ToString()); } Thread.Sleep(TimeSpan.FromSeconds(3)); t.Abort();//终止线程 Console.WriteLine("线程终止了"); Console.WriteLine(t.ThreadState.ToString()); Console.WriteLine(t1.ThreadState.ToString()); } static void PrintNumberWithdelay() { Console.WriteLine("开始啦..."); Console.WriteLine(Thread.CurrentThread.ThreadState.ToString()); for (int i = 0; i < 10; i++) { Thread.Sleep(TimeSpan.FromSeconds(1)); Console.WriteLine(i); } } static void DoNothing() { Thread.Sleep(TimeSpan.FromSeconds(2)); }
当主程序定义了两个不同的线程,一个将会被终止,而另一个则会成功运行。线程状态位于Thread 对象的ThreadState属性中。ThreadState属性是一个C#枚举对象。
刚开始线程状态为Unstarted ,然后启动线程,并估计在一周期为30的迭代的区间中,线程状会从Running变为WitSleepJoin。
线程优先级
class ThreadSample { private bool _isStopped = false; public void Stop() { _isStopped = true; } public void CountNumbers() { long counter = 0; while (!_isStopped) { counter++; } Console.WriteLine("{0} 和 {1,11}" + " count={2,13}", Thread.CurrentThread.Name, Thread.CurrentThread.Priority, counter.ToString("NO")); } } class Program { static void RunThreads() { var sample = new ThreadSample(); var threadOne = new Thread(sample.CountNumbers); threadOne.Name = "ThreadOne"; var threadTwo = new Thread(sample.CountNumbers); threadTwo.Name = "ThreadTwo"; threadOne.Priority = ThreadPriority.Highest;//优先级较高 threadTwo.Priority = ThreadPriority.Lowest;//优先级较低 threadOne.Start(); threadTwo.Start(); Thread.Sleep(TimeSpan.FromSeconds(2)); sample.Stop(); } static void Main(string[] args) { Console.WriteLine("线程状态:{0}",Thread.CurrentThread.Priority); Console.WriteLine("开始"); RunThreads(); Thread.Sleep(TimeSpan.FromSeconds(3)); Console.WriteLine("模拟CPU单核计算"); //让操作系统运行在第一个CPU第一个核心上 Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1); RunThreads();//运行时间很长 Console.WriteLine("线程终止了"); } }
此程序只用于演示,通常中无需使用这种方式。
前台线程和后台线程
class ThreadSample { private readonly int _iterations; public ThreadSample(int iterations) { _iterations = iterations; } public void CountNumbers() { for (int i = 0; i < _iterations; i++) { Thread.Sleep(TimeSpan.FromSeconds(0.5)); Console.WriteLine("{0} 和 {1}", Thread.CurrentThread.Name, i); } } } class Program { static void Main(string[] args) { var samppleforegroud = new ThreadSample(10); var sampplebackgroup = new ThreadSample(20); var threadOne = new Thread(samppleforegroud.CountNumbers); threadOne.Name = "前台"; var threadTwo = new Thread(sampplebackgroup.CountNumbers); threadTwo.Name = "后台"; threadTwo.IsBackground = true; threadOne.Start(); threadTwo.Start(); } }
显示创建的是前台线程, ThreadTwo是后台线程 ,通过配置第一个线程会比第二个线程先完成,前台线程如果执行完毕,那么也会把后台线程终止掉。
向线程传递参数
class ThreadSample { private readonly int _iterations; public ThreadSample(int iterations) { _iterations = iterations; } public void CountNumbers() { for (int i = 0; i < _iterations; i++) { Thread.Sleep(TimeSpan.FromSeconds(0.5)); Console.WriteLine("{0} 和 {1}", Thread.CurrentThread.Name, i); } } } class Program { static void Count(object i) { CountNumbers((int)i); } static void CountNumbers(int number) { Console.WriteLine(number); } static void PrintNumber(int number) { Console.WriteLine(number); } static void Main(string[] args) { var samppleforegroud = new ThreadSample(10); var threadOne = new Thread(samppleforegroud.CountNumbers); threadOne.Name = "One"; threadOne.Start(); threadOne.Join(); Console.WriteLine("------------"); var threadTwo = new Thread(Count); threadTwo.Name = "Two"; threadTwo.Start(8); threadTwo.Join(); var threaThree = new Thread(() => CountNumbers(12)); threaThree.Name = "Three"; threaThree.Start(); threaThree.Join(); int i = 10; var threaFour = new Thread(() => PrintNumber(i)); i = 20; var threaFour1 = new Thread(() => PrintNumber(i)); threaFour.Start(); threaFour1.Start(); } }
使用Lock
abstract class CountBase { public abstract void Increment(); public abstract void Decrement(); } class Counter : CountBase { public int Count { get; private set; } public override void Increment() { Count++; } public override void Decrement() { Count--; } } class CounterWithLock : CountBase { private readonly object _syncroot = new object(); public int Count { get; private set; } public override void Increment() { lock (_syncroot) { Count++; } } public override void Decrement() { lock (_syncroot) { Count--; } } } class Program { static void TestCouner(CountBase c) { for (int i = 0; i < 100000; i++) { c.Increment(); c.Decrement(); } } static void Main(string[] args) { var c = new Counter(); var t1 = new Thread(() => TestCouner(c)); var t2 = new Thread(() => TestCouner(c)); var t3 = new Thread(() => TestCouner(c)); t1.Start(); t2.Start(); t3.Start(); t1.Join(); t2.Join(); t3.Join(); Console.WriteLine(c.Count); var c1 = new CounterWithLock(); t1 = new Thread(() => TestCouner(c)); t2 = new Thread(() => TestCouner(c)); t3 = new Thread(() => TestCouner(c)); t1.Start(); t2.Start(); t3.Start(); t1.Join(); t2.Join(); t3.Join(); Console.WriteLine(c.Count); } }
使用Monitor 锁定资源
为了避免死锁,则使用Monitor 类 来避免死锁。
Monitor.TryEnter(lock1,TimeSpan.FromSeconds(5));