C# 线程--第二线程方法

概述

  上一章节中和大家分享了线程的基础使用方法。在这一章中来和大家分享线程的一些常用方法。

  主要包括:线程阻塞,线程终止,线程锁三方面。

Thread 的 Sleep 和 Join 方法

 Thread.Sleep:将当前线程阻塞指定的毫秒数。

Console.WriteLine("主线程执行时间:{0}", DateTime.Now.ToString());

Thread.Sleep(4000);  //阻塞4s

Console.WriteLine("主线程执行时间:{0}", DateTime.Now.ToString());

输出结果:

两次打印输出间隔为:4秒。线程阻塞以毫秒为单位。

Sleep也支持TimeSpan,将当前线程阻塞指定的时间。

Thread.Join:阻塞调用线程,直到某个线程终止时为止。

第一次看到msdn的解释一下子没有反应过来。这里我们可以理解为:分别开启三个线程t1,t2,t3对t1,t2,t3依次调用Join后,程序会先把线程t1执行完后,在执行线程t2的内容..以此类推到t3。

如下代码所示:

 1 var watch = Stopwatch.StartNew();

 2 Thread t1 = new Thread(() =>

 3 {

 4     Thread.Sleep(4000);

 5     Console.WriteLine("t1 is ending.");

 6 });

 7 t1.Start();

 8 t1.Join();

 9 Console.WriteLine("t1.Join() returned.");

10 

11 Thread t2 = new Thread(() =>

12 {

13     Thread.Sleep(1000);

14     Console.WriteLine("t2 is ending.");

15 });

16 t2.Start();

17 t2.Join();

18 Console.WriteLine("t2.Join() returned.");

19 

20 Console.WriteLine("总结:Join()会阻塞调用线程直到调用线程结束." + watch.ElapsedMilliseconds);    

输出结果:

C# 线程--第二线程方法

程序先执行线程t1里的内容,让线程阻塞4秒,因为线程t1调用Join()方法阻塞调用线程,直到t1线程执行完成。

然后打印出“t1.Join() returned.”。在执行t2线程,直到t2线程执行完后才执行主线程打印的内容。

这里不难看出他们是按顺序来执行的。

 

如果我们不使用join()方法看看他的输出结果会是怎么样:

var watch = Stopwatch.StartNew();

Thread t1 = new Thread(() =>

{

    Thread.Sleep(4000);

    Console.WriteLine("t1 is ending.");

});

t1.Start();

//t1.Join();

Console.WriteLine("t1.Join() returned.");



Thread t2 = new Thread(() =>

{

    Thread.Sleep(1000);

    Console.WriteLine("t2 is ending.");

});

t2.Start();

//t2.Join();

Console.WriteLine("t2.Join() returned.");



Console.WriteLine("总结:Join()会阻塞调用线程直到调用线程结束." + watch.ElapsedMilliseconds);

输出结果:

C# 线程--第二线程方法

此时主线程会先开启t1线程,t1被阻塞4秒 。所以t1线程里的内容没有被打印出来,会在4秒后打印。

这时主线程不会等待t1线程完成后在执行下面代码,主线程会继续向下执行打印出“t1.Join() returned.”

然后开启t2线程,t2线程同样也会被阻塞了1秒。

主线程会继续向下执行打印出其他内容。最后陆续由线程t2,线程t1打印出各自对应信息。

Thread 的 Abort 和 Interrupt

Thread.Abort:在调用此方法的线程上引发 ThreadAbortException,以开始终止此线程的过程。 调用此方法通常会终止线程。

 1 Thread t1 = new Thread(() =>

 2 {

 3     for (int i = 0; i < 4; i++)

 4     {

 5         try

 6         {

 7             Thread.Sleep(400);

 8         }

 9         catch (ThreadAbortException ex)

10         {

11             Console.WriteLine("Abort终止线程.当前线程名称:{0}.状态:{1}", Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState);

12         }

13         Console.WriteLine("我在运行着!");

14     }

15 

16 });

17 t1.Name = "t1";

18 t1.Start();

19 Thread.Sleep(1000);

20 t1.Abort();

21 Console.WriteLine("当前线程名称:{0}.状态:{1}", t1.Name, t1.ThreadState);

输出结果:

C# 线程--第二线程方法

开启t1线程,阻塞800毫秒打印了二次“我在运行着!”,准备运行第三次时。

阻塞1000毫秒的主线程调用Abort()方法直接把t1线程给干掉了.他再也没有站起来执行第四次打印。

当前t1线程直接被干掉。

Thread.Abort:中断处于 WaitSleepJoin 线程状态的线程。

 1 Thread t2 = new Thread(() =>

 2 {

 3     for (int i = 0; i < 4; i++)

 4     {

 5         try

 6         {

 7             Thread.Sleep(400);

 8             Console.WriteLine("我在运行着!");

 9         }

10         catch (ThreadInterruptedException ex)

11         {

12             Console.WriteLine("Interrupt终止线程.当前线程名称:{0}.状态:{1}", Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState);

13         }

14     }

15 });

16 t2.Name = "t2";

17 t2.Start();

18 Thread.Sleep(1000);

19 t2.Interrupt();

20 Console.WriteLine("当前线程名称:{0}.状态:{1}", t2.Name, t2.ThreadState);

21 Console.Read();

输出结果:

C# 线程--第二线程方法

开启t2线程,阻塞800毫秒打印了二次“我在运行着!”,准备运行第三次时。

阻塞1000毫秒的主线程调用Interrupt()方法把t2线程第三次阻塞中断,但t2线程并未被终止,继续在运行。

直到线程运行结束。

线程锁

Monitor.Enter() 和 Monitor.Exit() :在指定对象上获取排他锁。

先来看一下多线程在访问共享变量未加锁的情况:

 1 int number = 0;

 2 //没加锁

 3 for (int i = 0; i < 10; i++)

 4 {

 5     new Thread(() =>

 6     {

 7         Thread.Sleep(1000);  //堵塞线程.不然线程执行时间太短,体现不出并发效果

 8         Console.WriteLine(number);

 9         number++;

10     }).Start();

11 }

输出结果:

C# 线程--第二线程方法

我们发现开启10个线程去访问一个共享变量number,在没有加锁的情况下有5个线程访问到的值都是:0。

当多个线程存在并发的时,难免会碰到相互冲突的事情。这个时候我们就会用到锁。

Lock()方法在MSIL中会被编译成 Monitor.Enter()和Monitor.Exit()。

在来看看多线程在访问共享变量加锁的情况:

 1 //加锁

 2 int number = 0;

 3 object objLock = new object();

 4 for (int i = 0; i < 10; i++)

 5 {

 6     new Thread(() =>

 7     {

 8         Thread.Sleep(100);  //堵塞线程.不然线程执行时间太短,体现不出并发效果

 9 

10         Monitor.Enter(objLock);

11         Console.WriteLine(number);

12         number++;

13         Monitor.Exit(objLock);

14     }).Start();

15 }

输出结果:

C# 线程--第二线程方法

加锁后我们发现多线程在访问共享变量采用的是排他模式。

每次访问共享变量都只有一个线程,其他线程只能等待别的线程访问完成后才能进行访问。[Monitor.Enter()和Monitor.Exit()必须是成对使用.]

 

Monitor.Wait() / Monitor.Pulse() : 释放对象上的锁并阻止当前线程,直到它重新获取该锁。/通知等待队列中的线程锁定对象状态的更改。

 1 object objLock = new object();

 2 new Thread(() => 

 3 {

 4     Thread.Sleep(1000);

 5     Monitor.Enter(objLock);

 6 

 7     Console.WriteLine("我是第一个出现");

 8     Console.WriteLine("我是第二个出现");

 9     Monitor.Wait(objLock);

10     Console.WriteLine("完成1");

11     Monitor.Pulse(objLock);

12 

13     Monitor.Exit(objLock);

14     

15 }).Start();

16 

17 

18 new Thread(() =>

19 {

20     Thread.Sleep(2000);

21     Monitor.Enter(objLock);

22 

23     Monitor.Pulse(objLock);

24     Console.WriteLine("我是第三个出现");

25     Console.WriteLine("我是第四个出现");

26     Monitor.Wait(objLock);

27     Console.WriteLine("完成2");

28 

29     Monitor.Exit(objLock);

30 }).Start();

输出结果:

C# 线程--第二线程方法

你可能感兴趣的:(C#)