线程----code
阅读原文时间:2023年07月11日阅读:1

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace 多线程
{
class Program
{
//public static void Sleep(TimeSpan timeout); //时间段
//public static void Sleep(int millisecondsTimeout); //毫秒数
//public void Join();
//public bool Join(int millisecondsTimeout); //毫秒数
//public bool Join(TimeSpan timeout);       //时间段
public static void ThreadMethod(object parameter)//方法内可以有参数,也可以没有参数
{
Console.WriteLine(parameter);
Console.WriteLine("{0}开始执行。", Thread.CurrentThread.Name);
}
public static void ThreadMethod2(object parameter)//方法内可以有参数,也可以没有参数
{
Console.WriteLine("我是:{0},我要终止了", Thread.CurrentThread.Name);
//开始终止线程
Thread.CurrentThread.Abort();//引发 System.Threading.ThreadAbortException
//下面的代码不会执行
; i < ; i++)
{
Console.WriteLine("我是:{0},我循环{1}次", Thread.CurrentThread.Name, i);
}
}
public static void ThreadMethod3(object parameter)
{
try
{
Console.WriteLine("我是:{0},我要终止了", Thread.CurrentThread.Name);
//开始终止线程
Thread.CurrentThread.Abort();
}
catch (ThreadAbortException ex)
{
Console.WriteLine("我是:{0},我又恢复了", Thread.CurrentThread.Name);
//恢复被终止的线程
Thread.ResetAbort();
}
; i < ; i++)
{
Console.WriteLine("我是:{0},我循环{1}次", Thread.CurrentThread.Name, i);
}
}
public static void ThreadMethod4(object parameter)
{
; i < ; i++)
{
Console.WriteLine("我是:{0},我循环{1}次", Thread.CurrentThread.Name, i);
Thread.Sleep(); //休眠300毫秒
}
}
public static void ThreadMethod5(object parameter)
{
; i < ; i++)
{
Console.WriteLine("我是:{0},我循环{1}次", Thread.CurrentThread.Name, i);
Thread.Sleep(); //休眠300毫秒
}
}
public static void ThreadMethod6(object parameter)
{
Thread.CurrentThread.Suspend(); //挂起当前线程
; i < ; i++)
{
Console.WriteLine("我是:{0},我循环{1}次", Thread.CurrentThread.Name, i);
}
}
public static void ThreadMethod7(object parameter)
{
; i < ; i++)
{
Console.Write(Thread.CurrentThread.Name);
}
}
public static void ThreadMethod8(object parameter)
{
; i < ; i++)
{
Console.WriteLine("我是:{0},我循环{1}次", Thread.CurrentThread.Name, i);
Thread.Sleep();
}
}
public void ThreadMethod9(object parameter)
{
lock (this) //添加lock关键字
{
; i < ; i++)
{
Console.WriteLine("我是:{0},我循环{1}次", Thread.CurrentThread.Name, i);
Thread.Sleep();
}
}
}
public void ThreadMethod10(object parameter)
{
lock (this)
{
; i < ; i++)
{
Console.WriteLine("我是:{0},我循环{1}次", Thread.CurrentThread.Name, i);
Thread.Sleep();
}
}
}
public void ThreadMethod11(object parameter)
{
lock (obj)
{
; i < ; i++)
{
Console.WriteLine("我是:{0},我循环{1}次", Thread.CurrentThread.Name, i);
Thread.Sleep();
}
}
}
//static object obj = new object();
public static void ThreadMethod12()
{
Monitor.Enter(obj); //Monitor.Enter(obj) 锁定对象
try
{
; i < ; i++)
{
Console.Write(Thread.CurrentThread.Name);
}
}
catch (Exception ex) { }
finally
{
Monitor.Exit(obj); //释放对象
}
}
//static object obj = new object();
public static void ThreadMethod13()
{
//Monitor.Enter(obj);
); //设置1S的超时时间,如果在1S之内没有获得同步锁,则返回false
//上面的代码设置了锁定超时时间为1秒,也就是说,在1秒中后,
//lockObj还未被解锁,TryEntry方法就会返回false,如果在1秒之内,lockObj被解锁,TryEntry返回true。我们可以使用这种方法来避免死锁
//死锁的本质是一种僵持状态,是多个主体对于资源的争用而导致的。(由于竞争资源或者由于彼此通信而造成的一种阻塞的现象)
//永远在互相等待的进程称为死锁进程
try
{
//
//
if (flag)
{
; i < ; i++)
{
Console.Write(Thread.CurrentThread.Name);
}
//
}
}
catch (Exception ex)
{

        }
        finally
        {
            //
            if (flag)
                Monitor.Exit(obj);
        }
    }
    public static void ThreadMethod14()
    {
        //Monitor.Enter(obj);
        //Monitor.TryEnter(obj, millisecondsTimeout) 避免死锁
        //lock(obj)块(非常容易发生死锁) Monitor.TryEnter(obj, millisecondsTimeout)
        //二者的第一个参数意义相同,而后者还可以设置等待超时时间,一旦在限定的时间内无法获得锁,那么 TryEnter 就会返回  false。这样就不会造成死锁,无法获得资源,业务程序可以采取重试或抛异常的方式进行善后处理。
        );   //设置0.5S的超时时间,如果在1S之内没有获得同步锁,则返回false
        //上面的代码设置了锁定超时时间为1秒,也就是说,在1秒中后,
        //lockObj还未被解锁,TryEntry方法就会返回false,如果在1秒之内,lockObj被解锁,TryEntry返回true。我们可以使用这种方法来避免死锁
        //死锁的本质是一种僵持状态,是多个主体对于资源的争用而导致的。(由于竞争资源或者由于彼此通信而造成的一种阻塞的现象)
        //永远在互相等待的进程称为死锁进程
        try
        {
            //
            //
            if (flag)
            {
                Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));
                ; i < ; i++)
                {
                    Console.Write(Thread.CurrentThread.Name);
                }
                //
                Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));
            }
        }
        catch (Exception ex)
        {

        }
        finally
        {
            //
            if (flag)
                Monitor.Exit(obj);
        }
    }

    //死锁例子
    public static object lock\_A = new object();
    public static object lock\_B = new object();
    public void ThreadMethod15()
    {////在这里打了断点调试之后,主线程 早已走完 所以就 不会产生死锁了
        lock (lock\_A)
        {
            Thread.Sleep();
            Console.WriteLine("我是lock\_A,我想要lock\_B");
            lock (lock\_B)
            {
                Console.WriteLine("没出现这句话表示死锁了");
            }
        }
    }

    private static Mutex mutet = new Mutex();//外部无法对该变量进行访问,直到被释放 还是关键看锁的谁
    public static void ThreadMethod16(object val)
    {
        //Mutex mutet = new Mutex();  这样就不同步 见上面例子
        //对其它类实例无影响
        mutet.WaitOne();    //获取锁
        ; i < ; i++)
        {
            Console.Write(Thread.CurrentThread.Name);
        }
        mutet.ReleaseMutex();  //释放锁
    }

    public static void ThreadMethod17(object val)
    {
        ; i <= ; i++)
        {
             == )
            {
                //Console.Write(Thread.CurrentThread.Name);
                Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
            }
        }
    }

    private static object obj = new object();
    ////我们的控制台程序入口是main函数。它所在的线程即是主线程
    //它的功能主要 是产生新的线程和执行程序
    static void Main(string\[\] args)
    {
        #region 多线程的创建demo1
        ////我们的控制台程序入口是main函数。它所在的线程即是主线程

        ////创建出新的线程(子线程)
        ////我们通过Thread类来创建子线程,Thread类有 ThreadStart 和 ParameterizedThreadStart类型的委托参数,我们也可以直接写方法的名字。线程执行的方法可以传递参数(可选),参数的类型为object,写在Start()里。
        //Thread thread = new Thread(ThreadMethod); //执行的必须是无返回值的方法
        ////thread.Name = "子线程";
        ////调用Start方法使得线程进入就绪状态,得到系统资源后就执行,在执行过程中可能有等待、休眠、死亡和阻塞四种状态。正常执行结束时间片后返回到就绪状态。如果调用Suspend方法会进入等待状态,调用Sleep或者遇到进程同步使用的锁机制而休眠等待。

        ////thread.Start("王建"); ; //在此方法内传递参数,类型为object,发送和接收涉及到拆装箱操作
        //thread.Start();
        //Console.ReadKey();
        #endregion

        #region demo2 线程详情
        //Thread thread = new Thread(ThreadMethod);     //执行的必须是无返回值的方法
        //thread.Name = "子线程";
        //thread.Start();
        //StringBuilder threadInfo = new StringBuilder();
        //threadInfo.Append(" 线程当前的执行状态: " + thread.IsAlive);
        //threadInfo.Append("\\n 线程当前的名字: " + thread.Name);
        //threadInfo.Append("\\n 线程当前的优先级: " + thread.Priority);
        //threadInfo.Append("\\n 线程当前的状态: " + thread.ThreadState);
        //Console.Write(threadInfo);
        //Console.ReadKey();
        #endregion

        #region demo3 Abort()方法 终止线程
        ////调用此方法强制停止正在执行的线程,它会抛出一个ThreadAbortException异常从而导致目标线程的终止。
        //Thread thread = new Thread(ThreadMethod2);     //执行的必须是无返回值的方法
        //thread.Name = "小A";
        //thread.Start();
        //Console.ReadKey();
        ////结果  循环没有被执行
        #endregion

        #region demo4 ResetAbort()方法
        ////Abort方法可以通过跑出ThreadAbortException异常中止线程,而使用ResetAbort方法可以取消中止线程的操作,下面通过代码演示使用 ResetAbort方法。
        //Thread thread = new Thread(ThreadMethod3);     //执行的必须是无返回值的方法
        //thread.Name = "小A";
        //thread.Start();
        //Console.ReadKey();
        #endregion

        #region demo5 Sleep()方法
        ////Sleep()方法调已阻塞线程,是当前线程进入休眠状态,在休眠过程中占用系统内存但是不占用系统时间,当休眠期过后,继续执行,
        //Thread threadA = new Thread(ThreadMethod4);     //执行的必须是无返回值的方法
        //threadA.Name = "小A";
        //threadA.Start();
        //Console.ReadKey();
        ////可以清楚的看到每次循环之间相差300毫秒的时间
        #endregion

        #region demo6 join()方法
        ////Join方法主要是用来阻塞调用线程,直到某个线程终止或经过了指定时间为止。官方的解释比较乏味,通俗的说就是创建一个子线程,给它加了这个方法,其它线程就会暂停执行,直到这个线程执行完为止才去执行(包括主线程)。她的方法声明如下:
        //Thread threadA = new Thread(ThreadMethod5);     //执行的必须是无返回值的方法
        //threadA.Name = "小A";
        //Thread threadB = new Thread(ThreadMethod5);     //执行的必须是无返回值的方法
        //threadB.Name = "小B";
        //threadA.Start();
        ////
        //threadA.Join(); //2现在我们把代码中的  ThreadA.join() threadB.Join();方法注释取消,首先程序中有三个线程,ThreadA、ThreadB和主线程,首先主线程先阻塞,然后线程ThreadB阻塞,ThreadA先执行,执行完毕以后ThreadB接着执行,最后才是主线程执行。

        //threadB.Start();
        ////
        //threadB.Join();

        //for (int i = 0; i < 10; i++)
        //{
        //    Console.WriteLine("我是:主线程,我循环{1}次", Thread.CurrentThread.Name, i);
        //    Thread.Sleep(300);          //休眠300毫秒
        //}
        //Console.ReadKey();
        ////1因为线程之间的执行是随机的,所有执行结果和我们想象的一样,杂乱无章!但是说明他们是同时执行的。
        #endregion

        #region demo7 Suspent()和Resume()方法
        ////其实在C# 2.0以后, Suspent()和Resume()方法已经过时了。suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被”挂起”的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend()。
        //Thread threadA = new Thread(ThreadMethod6); //执行的必须是无返回值的方法
        //threadA.Name = "小A";
        //threadA.Start();
        //Thread.Sleep(3000);         //休眠3000毫秒
        //threadA.Resume();           //继续执行已经挂起的线程
        //Console.ReadKey();
        ////执行上面的代码。窗口并没有马上执行 ThreadMethod方法输出循环数字,而是等待了三秒钟之后才输出,因为线程开始执行的时候执行了Suspend()方法挂起。然后主线程休眠了3秒钟以后又通过Resume()方法恢复了线程threadA。
        #endregion

        #region demo8 线程的优先级
        ////如果在应用程序中有多个线程在运行,但一些线程比另一些线程重要,这种情况下可以在一个进程中为不同的线程指定不同的优先级。线程的优先级可以通过Thread类Priority属性设置,Priority属性是一个ThreadPriority型枚举,列举了5个优先等级:AboveNormal、BelowNormal、Highest、Lowest、Normal。公共语言运行库默认是Normal类型的。
        //Thread threadA = new Thread(ThreadMethod7); //执行的必须是无返回值的方法
        //threadA.Name = "A";
        //Thread threadB = new Thread(ThreadMethod7); //执行的必须是无返回值的方法
        //threadB.Name = "B";
        //threadA.Priority = ThreadPriority.Highest;
        //threadB.Priority = ThreadPriority.BelowNormal;
        //threadB.Start();
        //threadA.Start();
        //Thread.CurrentThread.Name = "C";
        //ThreadMethod7(new object());
        //Console.ReadKey();
        ////上面的代码中有三个线程,threadA,threadB和主线程,threadA优先级最高,threadB优先级最低。这一点从运行结果中也可以看出,线程B 偶尔会出现在主线程和线程A前面。当有多个线程同时处于可执行状态,系统优先执行优先级较高的线程,但这只意味着优先级较高的线程占有更多的CPU时间,并不意味着一定要先执行完优先级较高的线程,才会执行优先级较低的线程。
        ////优先级越高表示CPU分配给该线程的时间片越多,执行时间就多
        ////优先级越低表示CPU分配给该线程的时间片越少,执行时间就少
        #endregion

        #region demo9 线程同步 使用Lock关键字实现线程同步
        ////什么是线程安全:线程安全是指在当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。

        ////线程有可能和其他线程共享一些资源,比如,内存,文件,数据库等。当多个线程同时读写同一份共享资源的时候,可能会引起冲突。这时候,我们需要引入线程“同步”机制,即各位线程之间要有个先来后到,不能一窝蜂挤上去抢作一团。线程同步的真实意思和字面意思恰好相反。线程同步的真实意思,其实是“排队”:几个线程之间要排队,一个一个对共享资源进行操作,而不是同时进行操作。
        ////为什么要实现同步呢,下面的例子我们拿著名的单例模式来说吧。看代码
        ////两个线程执行同一个方法
        //Thread threadA = new Thread(ThreadMethod8); //执行的必须是无返回值的方法
        //threadA.Name = "王文建";
        //Thread threadB = new Thread(ThreadMethod8); //执行的必须是无返回值的方法
        //threadB.Name = "生旭鹏";
        //threadA.Start();
        //threadB.Start();
        //Console.ReadKey();
        ////可以很清楚的看到,两个线程是在同时执行ThreadMethod这个方法,这显然不符合我们线程同步的要求。我们对代码进行修改如下:
        #endregion

        #region demo10 引出线程同步
        //Program pro = new Program();
        //Thread threadA = new Thread(pro.ThreadMethod9); //执行的必须是无返回值的方法
        ////Thread threadA = new Thread(new ThreadStart(pro.ThreadMethod9)); //但必须是无参无回方法
        //threadA.Name = "王文建";
        //Thread threadB = new Thread(pro.ThreadMethod9); //执行的必须是无返回值的方法
        //threadB.Name = "生旭鹏";
        //threadA.Start();
        //threadB.Start();
        //Console.ReadKey();
        ////通过添加了 lock(this) {...}代码,查看执行结果实现了我们想要的线程同步需求。但是我们知道this表示当前类实例的本身,那么有这么一种情况,我们把需要访问的方法所在的类型进行两个实例A和B,线程A访问实例A的方法ThreadMethod,线程B访问实例B的方法ThreadMethod,这样的话还能够达到线程同步的需求吗。
        #endregion

        #region demo11 创多个对象是否实现线程同步
        //Program pro1 = new Program();
        //Program pro2 = new Program();
        //Thread threadA = new Thread(pro1.ThreadMethod10); //执行的必须是无返回值的方法
        //threadA.Name = "王文建";
        //Thread threadB = new Thread(pro2.ThreadMethod10); //执行的必须是无返回值的方法
        //threadB.Name = "生旭鹏";
        //threadA.Start();
        //threadB.Start();
        //Console.ReadKey();
        ////我们会发现,线程又没有实现同步了!lock(this)对于这种情况是不行的!所以需要我们对代码进行修改!修改后的代码如下:
        #endregion

        #region demo12 lock(obj)
        //Program pro1 = new Program();
        //Program pro2 = new Program();
        //Thread threadA = new Thread(pro1.ThreadMethod11); //执行的必须是无返回值的方法
        //threadA.Name = "王文建";
        //Thread threadB = new Thread(pro2.ThreadMethod11); //执行的必须是无返回值的方法
        //threadB.Name = "生旭鹏";
        //threadA.Start();
        //threadB.Start();
        //Console.ReadKey();
        ////代码实现了我们的需求。那么 lock(this) 和lock(Obj)有什么区别呢?
        ////lock(this) 锁定 当前实例对象,如果有多个类实例的话,lock锁定的只是当前类实例,对其它类实例无影响。所有不推荐使用。
        ////lock(typeof(Model))锁定的是model类的所有实例。
        ////lock(obj)锁定的对象是全局的私有化静态变量。外部无法对该变量进行访问。
        ////lock 确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放。
        ////所以,lock的结果好不好,还是关键看锁的谁,如果外边能对这个谁进行修改,lock就失去了作用。所以一般情况下,使用私有的、静态的并且是只读的对象。

        ////总结:

        ////1、lock的是必须是引用类型的对象,string类型除外。

        ////2、lock推荐的做法是使用静态的、只读的、私有的对象。

        ////3、保证lock的对象在外部无法修改才有意义,如果lock的对象在外部改变了,对其他线程就会畅通无阻,失去了lock的意义。

        ////     不能锁定字符串,锁定字符串尤其危险,因为字符串被公共语言运行库 (CLR)“暂留”。 这意味着整个程序中任何给定字符串都只有一个实例,就是这同一个对象表示了所有运行的应用程序域的所有线程中的该文本。因此,只要在应用程序进程中的任何位置处具有相同内容的字符串上放置了锁,就将锁定应用程序中该字符串的所有实例。通常,最好避免锁定 public 类型或锁定不受应用程序控制的对象实例。例如,如果该实例可以被公开访问,则 lock(this) 可能会有问题,因为不受控制的代码也可能会锁定该对象。这可能导致死锁,即两个或更多个线程等待释放同一对象。出于同样的原因,锁定公共数据类型(相比于对象)也可能导致问题。而且lock(this)只对当前对象有效,如果多个对象之间就达不到同步的效果。lock(typeof(Class))与锁定字符串一样,范围太广了。
        #endregion

        #region demo13 使用Monitor类实现线程同步
        //// Lock关键字是Monitor的一种替换用法,lock在IL代码中会被翻译成Monitor.
        ////lock(obj)
        ////{
        ////   //代码段
        ////}
        ////就等同于
        ////Monitor.Enter(obj);  //在指定对象上获取排他锁。
        ////  //代码段
        ////Monitor.Exit(obj);  //释放指定对象上的排他锁。 

        //// Pulse 通知等待队列中的线程锁定对象状态的更改。
        //// PulseAll 通知所有的等待线程对象状态的更改。
        //// TryEnter(Object) 试图获取指定对象的排他锁。
        //// TryEnter(Object, Boolean) 尝试获取指定对象上的排他锁,并自动设置一个值,指示是否   得  到了该锁。
        //// Wait(Object) 释放对象上的锁并阻止当前线程,直到它重新获取该锁。

        ////常用的方法有两个,Monitor.Enter(object)方法是获取锁,Monitor.Exit(object)方法是释放锁,这就是Monitor最常用的两个方法,在使用过程中为了避免获取锁之后因为异常,致锁无法释放,所以需要在try{} catch(){}之后的finally{}结构体中释放锁(Monitor.Exit())。

        //Thread threadA = new Thread(ThreadMethod12);
        //threadA.Name = "A";
        //Thread threadB = new Thread(ThreadMethod12);
        //threadB.Name = "B";
        //threadA.Start();
        //threadB.Start();
        //Thread.CurrentThread.Name = "C";
        //ThreadMethod12();
        //Console.ReadKey();
        #endregion

        #region demo14 TryEnter(Object)和TryEnter()
        ////TryEnter(Object)和TryEnter() 方法在尝试获取一个对象上的显式锁方面和 Enter() 方法类似。然而,它不像Enter()方法那样会阻塞执行。如果线程成功进入关键区域那么TryEnter()方法会返回true. 和试图获取指定对象的排他锁。

        ////我们可以通过Monitor.TryEnter(monster, 1000),该方法也能够避免死锁的发生,我们下面的例子用到的是该方法的重载,Monitor.TryEnter(Object,Int32),。
        //Thread threadA = new Thread(ThreadMethod13); //执行的必须是无返回值的方法
        //threadA.Name = "A";
        //Thread threadB = new Thread(ThreadMethod13); //执行的必须是无返回值的方法
        //threadB.Name = "B";
        //threadA.Start();
        //threadB.Start();
        //Thread.CurrentThread.Name = "C";
        //ThreadMethod13();
        //Console.ReadKey();
        #endregion

        #region demo15 TryEnter(Object)和TryEnter() 避免死锁
        //Thread threadA = new Thread(ThreadMethod14); //执行的必须是无返回值的方法
        //threadA.Name = "A";
        //Thread threadB = new Thread(ThreadMethod14); //执行的必须是无返回值的方法
        //threadB.Name = "B";
        //threadA.Start();
        //threadB.Start();
        //Thread.CurrentThread.Name = "C";
        //ThreadMethod14();
        //Console.ReadKey();
        #endregion

        #region demo16 看看死锁例子
        //Program a = new Program();
        //Thread th = new Thread(new ThreadStart(a.ThreadMethod15));
        //th.Start();
        //lock (lock\_B)
        //{
        //    Console.WriteLine("我是lock\_B,我想要lock\_A");
        //    lock (lock\_A)
        //    {
        //        Console.WriteLine("没出现这句话表示死锁了");
        //    }
        //}
        //Console.WriteLine("没出现这句话表示死锁了");
        //Console.WriteLine("不走了");
        //Console.ReadKey();
        //Console.ReadKey();
        #endregion

        #region demo17 Monitor.Wait和Monitor()Pause()
        ////Wait(object)方法:释放对象上的锁并阻止当前线程,直到它重新获取该锁,该线程进入等待队列。
        ////Pulse方法:只有锁的当前所有者可以使用 Pulse 向等待对象发出信号,当前拥有指定对象上的锁的线程调用此方法以便向队列中的下一个线程发出锁的信号。接收到脉冲后,等待线程就被移动到就绪队列中。在调用 Pulse 的线程释放锁后,就绪队列中的下一个线程(不一定是接收到脉冲的线程)将获得该锁。
        ////另外:
        ////Wait 和 Pulse 方法必须写在 Monitor.Enter 和Moniter.Exit 之间。
        ////怪物类
        //Monster monster = new Monster(1000);
        ////物理攻击类
        //Play play1 = new Play() { Name = "无敌剑圣", Power = 100 };
        ////魔法攻击类
        //Play play2 = new Play() { Name = "流浪法师", Power = 120 };
        //Thread thread\_first = new Thread(play1.physicsExecute);    //物理攻击线程
        //Thread thread\_second = new Thread(play2.magicExecute);     //魔法攻击线程
        //thread\_first.Start(monster);
        //thread\_second.Start(monster);
        //Console.ReadKey();

        ////总结:
        ////  第一种情况:
        ////thread\_first首先获得同步对象的锁,当执行到 Monitor.Wait(monster);时,thread\_first线程释放自己对同步对象的锁,流放自己到等待队列,直到自己再次获得锁,否则一直阻塞。
        ////而thread\_second线程一开始就竞争同步锁所以处于就绪队列中,这时候thread\_second直接从就绪队列出来获得了monster对象锁,开始执行到Monitor.PulseAll(monster)时,发送了个Pulse信号。
        ////这时候thread\_first接收到信号进入到就绪状态。然后thread\_second继续往下执行到 Monitor.Wait(monster, 1000)时,这是一句非常关键的代码,thread\_second将自己流放到等待队列并释放自身对同步锁的独占,该等待设置了1S的超时值,当B线程在1S之内没有再次获取到锁自动添加到就绪队列。
        ////这时thread\_first从Monitor.Wait(monster)的阻塞结束,返回true。开始执行、打印。执行下一行的Monitor.Pulse(monster),这时候thread\_second假如1S的时间还没过,thread\_second接收到信号,于是将自己添加到就绪队列。
        ////thread\_first的同步代码块结束以后,thread\_second再次获得执行权, Monitor.Wait(m\_smplQueue, 1000)返回true,于是继续从该代码处往下执行、打印。当再次执行到Monitor.Wait(monster, 1000),又开始了步骤3。
        ////依次循环。。。。
        ////第二种情况:thread\_second首先获得同步锁对象,首先执行到Monitor.PulseAll(monster),因为程序中没有需要等待信号进入就绪状态的线程,所以这一句代码没有意义,当执行到 Monitor.Wait(monster, 1000),自动将自己流放到等待队列并在这里阻塞,1S 时间过后thread\_second自动添加到就绪队列,线程thread\_first获得monster对象锁,执行到Monitor.Wait(monster);时发生阻塞释放同步对象锁,线程thread\_second执行,执行Monitor.PulseAll(monster)时通知thread\_first。于是又开始第一种情况...

        ////Monitor.Wait是让当前进程睡眠在临界资源上并释放独占锁,它只是等待,并不退出,当等待结束,就要继续执行剩下的代码。

        #endregion

        #region demo18 使用Mutex类实现线程同步
        //// Mutex的突出特点是可以跨应用程序域边界对资源进行独占访问,即可以用于同步不同进程中的线程,这种功能当然这是以牺牲更多的系统资源为代价的。
        ////主要常用的两个方法:
        ////public virtual bool WaitOne()   阻止当前线程,直到当前 System.Threading.WaitHandle 收到信号获取互斥锁。
        ////public void ReleaseMutex()     释放 System.Threading.Mutex 一次。
        //Thread\[\] thread = new Thread\[3\];
        //for (int i = 0; i < 3; i++)
        //{
        //    thread\[i\] = new Thread(ThreadMethod16);
        //    thread\[i\].Name = i.ToString();
        //}
        //for (int i = 0; i < 3; i++)
        //{
        //    thread\[i\].Start();
        //}
        //Console.ReadKey();
        #endregion

        #region demo19 线程池
        //上面介绍了介绍了平时用到的大多数的多线程的例子,但在实际开发中使用的线程往往是大量的和更为复杂的,这时,每次都创建线程、启动线程。从性能上来讲,这样做并不理想(因为每使用一个线程就要创建一个,需要占用系统开销);从操作上来讲,每次都要启动,比较麻烦。为此引入的线程池的概念。
        //好处:
        //1.减少在创建和销毁线程上所花的时间以及系统资源的开销
        //2.如不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存以及”过度切换”。

        //在什么情况下使用线程池?
        //    1.单个任务处理的时间比较短
        //    2.需要处理的任务的数量大
        //线程池最多管理线程数量=“处理器数 \* 250”。也就是说,如果您的机器为2个2核CPU,那么CLR线程池的容量默认上限便是1000
        //通过线程池创建的线程默认为后台线程,优先级默认为Normal。
        //ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadMethod17), new object());    //参数可选
        ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadMethod17), new object());    //参数可选
        Console.ReadKey();

        #endregion
    }
}

//public class Singleton
//{
//    private static Singleton instance;
//    private Singleton()   //私有函数,防止实例
//    {

//    }
//    public static Singleton GetInstance()
//    {
//        if (instance == null)
//        {
//            instance = new Singleton();
//        }
//        return instance;
//    }
//}
//       单例模式就是保证在整个应用程序的生命周期中,在任何时刻,被指定的类只有一个实例,并为客户程序提供一个获取该实例的全局访问点。但上面代码有一个明显的问题,那就是假如两个线程同时去获取这个对象实例,那。。。。。。。。

//我们队代码进行修改:
public class Singleton
{
    private static Singleton instance;
    private static object obj = new object();
    private Singleton()        //私有化构造函数
    {

    }
    public static Singleton GetInstance()
    {
        if (instance == null)
        {
            lock (obj)      //通过Lock关键字实现同步
            {//经过修改后的代码。加了一个 lock(obj)代码块。这样就能够实现同步了
                if (instance == null)
                {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

/// <summary>
/// 怪物类
/// </summary>
internal class Monster
{
    public int Blood { get; set; }
    public Monster(int blood)
    {
        this.Blood = blood;
        Console.WriteLine("我是怪物,我有{0}滴血", blood);
    }
}
/// <summary>
/// 攻击类
/// </summary>
internal class Play
{
    /// <summary>
    /// 攻击者名字
    /// </summary>
    public string Name { get; set; }
    /// <summary>
    /// 攻击力
    /// </summary>
    public int Power { get; set; }
    /// <summary>
    /// 法术攻击
    /// </summary>
    public void magicExecute(object monster)
    {
        Monster m = monster as Monster;
        Monitor.Enter(monster);
        )
        {
            Monitor.Wait(monster);
            Console.WriteLine("当前英雄:{0},正在使用法术攻击打击怪物", this.Name);
            if (m.Blood >= Power)
            {
                m.Blood -= Power;
            }
            else
            {
                m.Blood = ;
            }
            Thread.Sleep();
            Console.WriteLine("怪物的血量还剩下{0}", m.Blood);
            Monitor.PulseAll(monster);
        }
        Monitor.Exit(monster);
    }

    /// <summary>
    /// 物理攻击
    /// </summary>
    /// <param name="monster"></param>
    public void physicsExecute(object monster)
    {
        Monster m = monster as Monster;
        Monitor.Enter(monster);
        )
        {
            Monitor.PulseAll(monster);
            //当前拥有指定对象上的锁的线程调用此方法向所有等待获取该对象上的锁的线程发出信号。发送信号后,等待线程就被移动到就绪队列中。在调用 PulseAll 的线程释放锁后,就绪队列中的下一个线程将获取该锁。
            ))     //非常关键的一句代码
            {//
                Console.WriteLine("当前英雄:{0},正在使用物理攻击打击怪物", this.Name);
                if (m.Blood >= Power)
                {
                    m.Blood -= Power;
                }
                else
                {
                    m.Blood = ;
                }
                Thread.Sleep();
                Console.WriteLine("怪物的血量还剩下{0}", m.Blood);
            }
        }
        Monitor.Exit(monster);
    }
}

}

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace 线程池
{
public partial class Form1 : Form
{
//线程池中最多5个线程,执行一个方法60次,算5年总工资,如下:
//object locker = new object();
; //总收入
public Form1()
{
InitializeComponent();
}

    private void button1\_Click(object sender, EventArgs e)
    {
        ThreadPool.SetMaxThreads(, );//最多5个线程
        ; i < ; i++) // 执行12\*5次,算5年总收入
        {
            ThreadPool.QueueUserWorkItem(Run, ); // QueueUserWorkItem 把要执行的方法Run()添加进线程池
        }
        Thread.Sleep( \* ); //休眠1秒
        MessageBox.Show(money.ToString());  //输出总金额
    }

    private void Run(object mny)
    {
        money += int.Parse(mny.ToString());
    }
}

}