zoukankan      html  css  js  c++  java
  • 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);    

    输出结果:

    程序先执行线程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);

    输出结果:

    此时主线程会先开启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);

    输出结果:

    开启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();

    输出结果:

    开启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 }

    输出结果:

    我们发现开启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 }

    输出结果:

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

    每次访问共享变量都只有一个线程,其他线程只能等待别的线程访问完成后才能进行访问。[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();

    输出结果:

  • 相关阅读:
    02-MySQL的安装和管理
    01-pymysql模块的安装
    异常处理
    USACO 2015 Feb Censoring
    玄武密码(bzoj4327)(JSOI2012)
    浅谈AC自动机
    Equation
    JOI五子棋
    浅谈Tarjan
    年轮蛋糕JOI2014Final
  • 原文地址:https://www.cnblogs.com/caokai520/p/4303459.html
Copyright © 2011-2022 走看看