多线程状态与优先级、线程同步与Monitor类、死锁

一、线程状态

二、线程优先级

三、初步尝试多线程

 class Program
    {
        static void Main(string[] args)
        {
            while (true)
            {

            MessagePrinter p1=new MessagePrinter();
            Thread t1 = new Thread(new ThreadStart(p1.Print));
            t1.Name = "t1";
            t1.Priority = ThreadPriority.Highest;

            MessagePrinter p2 = new MessagePrinter();
            Thread t2 = new Thread(new ThreadStart(p2.Print));
            t2.Name = "t2";
            t2.Priority = ThreadPriority.Lowest;

            MessagePrinter p3 = new MessagePrinter();
            Thread t3 = new Thread(new ThreadStart(p3.Print));
            t3.Name = "t3";
            t2.Priority = ThreadPriority.Lowest;

            Console.WriteLine("线程启动:
");

            t1.Start();
            t2.Start();
            t3.Start();


            Console.WriteLine("线程结束:
");
            string r= Console.ReadLine();
            if (r == "1")
                break;
            }
        }
    }


    class MessagePrinter
    {
        private int _sleepTime;
        private static Random _random=new Random();

        public MessagePrinter()
        {
            _sleepTime = _random.Next(5001);
        }

        public void Print()
        {
            Thread current = Thread.CurrentThread;
            Console.WriteLine(string.Format("线程:{0},即将进入休眠状态:{1}毫秒", current.Name, _sleepTime));
            Thread.Sleep(_sleepTime);
            Console.WriteLine(string.Format("线程:{0},结束休眠", current.Name));
           
        }
    }
初识多线程

四、线程同步 与Monitor类

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

/*
 * 
 * 线程同步:
 *   1、作用:是保证多个线程之间同步处理【共享数据】的
 *   2、使用Monitor:
 *         > Enter:占用当前对象锁
 *         > Wait:等待
 *         > Pulse:让下一个其它线程开始运行
 *         > Exit:解除占用对象锁
 *   3、一定要将Exit放到TryCatch块的finialy中,防止在占用锁后,出现异常,然后没有释放锁,导致死锁
 *   4、使用Lock加锁,替代Enter和Exit;
 *   
 *   死锁的几种情况:
 *   1、互等死:线程之间互相引用,导致都不能释放
 *   2、等到死:线程使用Wait后,再没有其它线程使用Pulse或PulseAll进行唤醒
 *   3、异常死:在某个线程占用锁后,发生了异常,没有执行Exit释放锁操作,导致死锁
 * 
 * */
namespace 线程同步与Monitor类
{
    class Program
    {
        static void Main(string[] args)
        {

            #region 非线程同步

           // IBuffer buffer = new UnSynchronuzedBuffer();//非线程同步
            //IBuffer buffer = new SynchronizedBuffer();//线程同步
            IBuffer buffer = new CircularBuffer();//增加缓冲区空间,从而增加效率
            Random r = new Random();

            Producer p = new Producer(buffer, r);
            Consumer c = new Consumer(buffer, r);

            Thread t1 = new Thread(new ThreadStart(p.Produce));
            t1.Name = "Producer";

            Thread t2 = new Thread(new ThreadStart(c.Consume));
            t2.Name = "Consumer";

            t1.Start();
            t2.Start();

            #endregion

            #region 线程同步

            #endregion

            Console.ReadLine();
        }
    }

    #region 共享区
    public interface IBuffer
    {
        int Buffer
        {
            get;
            set;
        }
    }

    /// <summary>
    /// 非线程同步缓冲区
    /// </summary>
    public class UnSynchronuzedBuffer : IBuffer
    {
        private int _Buffer=-1;

        public int Buffer
        {
            get
            {
                Console.WriteLine(string.Format("       线程:{0},读取 {1} ",Thread.CurrentThread.Name,_Buffer));
                return _Buffer;
            }
            set
            {
                Console.WriteLine(string.Format("线程:{0},写入 {1} ", Thread.CurrentThread.Name, value));
                 _Buffer=value;
            }
        }

    }

    /// <summary>
    /// 线程同步,使用Monitor
    /// </summary>
    public class SynchronizedBuffer : IBuffer
    {
        private int _Buffer = -1;
        private int occupiedBufferCount = 0;

        public int Buffer
        {
            get
            {
                //锁定当前对象
                Monitor.Enter(this);

                try
                {
                    //如果没有Buffer中没有更新数据,则进入线程等待
                    if (occupiedBufferCount == 0)
                    {
                        Console.WriteLine(string.Format("       线程:{0},试图读取缓冲区数据", Thread.CurrentThread.Name));

                        DisplayState(string.Format("       Buffer是空的,线程:{0}进行等待", Thread.CurrentThread.Name));
                   

                        Console.WriteLine("       ===读取等待..........");
                        //释放 对象锁,并进入 WaitSleepJoin状态,等待再次获取锁
                        //本线程再次进入Running状态时,继续向下执行。
                        Monitor.Wait(this);
                        Console.WriteLine("       ===读取结束-1..........");
                    }

              

                    --occupiedBufferCount;

                    Console.WriteLine(string.Format("       线程:{0},开始读取:{1}",Thread.CurrentThread.Name,_Buffer));

                    //如果有其它线程,进行唤醒
                    Monitor.Pulse(this);

                    //复制buffer的目的,是防止刚解锁,生成者就立马改变了数据
                    int bufferCopy=_Buffer;

                    //返回副本
                    return bufferCopy;
                }
                catch (Exception ex)
                {

                    throw;
                }
                finally
                {
                    //释放对象上的锁
                    Monitor.Exit(this);


                }
                
                

            }
            set
            {
                if (!Monitor.TryEnter(this, 2))
                {
                    Console.WriteLine("【set时,TryEnter失败,将使用Enter进入。】");
                    Monitor.Enter(this);
                }

                if (occupiedBufferCount == 1)
                {
                    Console.WriteLine(string.Format("线程:{0},试图写入", Thread.CurrentThread.Name));
                    DisplayState(string.Format("Buffer 已满,线程:{0}等待", Thread.CurrentThread.Name));


                    Console.WriteLine("===写入等待..........");
                    //等待缓冲区数据被读取后,再写入
                    Monitor.Wait(this);
                    Console.WriteLine("===写入等待结束-1..........");
                }


                _Buffer = value;

                ++occupiedBufferCount;

                Console.WriteLine(string.Format("线程:{0},写入{1}", Thread.CurrentThread.Name, value));

                Monitor.Pulse(this);
                Monitor.Exit(this);

            }
        }

        /// <summary>
        /// 展示当前的操作和Buffer的状态
        /// </summary>
        /// <param name="operation"></param>
        public void DisplayState(string operation)
        {
            Console.WriteLine(string.Format("{0,-35}{1,-9}{2}
", operation, _Buffer, occupiedBufferCount));
        }
    }

    /// <summary>
    /// 当生产者与消费者速度基本同步时,适当增加缓冲区空间,从而减少互等时间。
    /// 使用Lock代替Monitor.Enter和Exit方法
    /// </summary>
    public class CircularBuffer : IBuffer
    {
        private int[] _Buffer = {-1,-1,-1};
        private int occupiedBufferCount = 0;

        private int readLocation = 0;//当前读取位置
        private int writeLocation = 0;//当前写入位置

        public int Buffer
        {
            get
            {
                Monitor.Enter(this);
                try
                {
                    if (occupiedBufferCount == 0)
                    {
                        Console.WriteLine(string.Format("   【Read】缓冲区为空,进入等待状态"));
                        Monitor.Wait(this);
                        Console.WriteLine(string.Format("   【Read】等待结束"));

                    }

                    int readValueCopy = _Buffer[readLocation];
                    Console.Write(string.Format("   【Read】线程:{0},读取:{1}", Thread.CurrentThread.Name, readValueCopy));

                    --occupiedBufferCount;
                    readLocation = (readLocation + 1) % _Buffer.Length;
                    Console.WriteLine(CreateStateOutput());

                    Monitor.Pulse(this);
                    return readValueCopy;

                }
                catch (Exception ex)
                {
                    Console.WriteLine(string.Format("异常:{0}",ex.Message.ToString()));
                    return -1;
                }
                finally
                {
                    Monitor.Exit(this);
                }
            }
            set
            {
                //使用lock 代替 Monitor.Enter 和 Monitor.Exit()
                lock (this)
                {
                    if (occupiedBufferCount == _Buffer.Length)
                    {
                        Console.WriteLine(string.Format("缓冲区【已满】,进入等待状态"));
                        Monitor.Wait(this);
                    }

                    _Buffer[writeLocation] = value;
                    Console.Write(string.Format("已在位置{0},写入:{1}", writeLocation, value));
                    
                    occupiedBufferCount++;
                    writeLocation = (writeLocation + 1) % _Buffer.Length;

                    Console.WriteLine(CreateStateOutput());

                    Monitor.Pulse(this);
                }
            }
                
        }

        private String CreateStateOutput()
        {
            string output = "(buffer occupied:" + occupiedBufferCount + ")
buffers:";
            for (int i = 0; i < _Buffer.Length; i++)
                output += " " + string.Format("{0,2}", _Buffer[i]) + "  ";

            output += "
";
            output += "         ";

            for (int i = 0; i < _Buffer.Length; i++)
                output += "-----";

            output += "
";
            output += "         ";

            for (int i = 0; i < _Buffer.Length; i++)
            {
                if (i == writeLocation && i == readLocation)
                    output += " WR  ";
                else if (i == writeLocation)
                    output += " W   ";
                else if (i == readLocation)
                    output += " R   ";
                else
                    output += "     ";

            }

            output += "
";

            return output;

        }
    }
    #endregion

    #region 生产者/消费者
    public class Producer
    {
        private IBuffer sharedLocation;
        private Random random;

        public Producer(IBuffer buffer, Random random)
        {
            this.sharedLocation = buffer;
            this.random = random;
        }

        public void Produce()
        {
            for (int count = 1; count <= 10; count++)
            {
                Thread.Sleep(random.Next(1, 1001));
                sharedLocation.Buffer = count;
            }

            Console.WriteLine(string.Format("=====生产者线程:{0},已执行完毕!", Thread.CurrentThread.Name));
        }

    }

    public class Consumer
    {
        private IBuffer sharedLocation;
        private Random random;

        public Consumer(IBuffer buffer, Random random)
        {
            this.sharedLocation = buffer;
            this.random = random;
        }

        public void Consume()
        {
            int sum = 0;
            for (int count = 1; count <= 10; count++)
            {
                Thread.Sleep(random.Next(1, 1001));
                sum+= sharedLocation.Buffer;
            }

            Console.WriteLine(string.Format("=======消费者线程:{0},已执行完毕,SUM={1}!", Thread.CurrentThread.Name,sum));
        }
    }
    #endregion
}
线程同步与Monitor类

五、GUI与线程同步

* Window窗体控件不是线程安全的
* Control类的Invoke方法,就是将子线程中需要对 控件进行操作时,返回主线程中进行控件操作。

namespace GUI与多线程
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        RandomLetters l1 = null;
        RandomLetters l2 = null;
        RandomLetters l3 = null;

        private void Form1_Load(object sender, EventArgs e)
        {
            this.FormClosing += new FormClosingEventHandler(Form1_FormClosing);

             l1 = new RandomLetters(label1,textBox1);
             l2 = new RandomLetters(label2, textBox1);
             l3 = new RandomLetters(label3, textBox1);
            Thread t1 = new Thread(new ThreadStart(l1.GenerateRandomCharacters));
            t1.Name = "t1";
            t1.Start();
                       
            Thread t2 = new Thread(new ThreadStart(l2.GenerateRandomCharacters));
            t2.Name = "t2";
            t2.Start();
                      
            Thread t3 = new Thread(new ThreadStart(l3.GenerateRandomCharacters));
            t3.Name = "t3";
            t3.Start();
        }

        void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            System.Environment.Exit(System.Environment.ExitCode);
        }

        private void checkBox_CheckedChanged(object sender, EventArgs e)
        {
            if (sender == checkBox1)
                l1.Toggle();
            else if (sender == checkBox2)
                l2.Toggle();
            else if (sender == checkBox3)
                l3.Toggle();
        }



    }


    #region GUI线程处理
    public class RandomLetters
    {
        private static Random random = new Random();
        private Label output = null;//需要输出到的Label
        private bool suspended = false;//是否暂停线程
        private string threadName;

        public RandomLetters(Label lbl)
        {
            this.output = lbl;
        }

        /// <summary>
        /// 定义界面需要展示的字符
        /// </summary>
        /// <param name="displayChar"></param>
        private delegate void DisplayDelegate(char displayChar);

        private void DisplayChar(char displayChar)
        {
            output.Text = threadName + ":" + displayChar;
        }

        public void GenerateRandomCharacters()
        {
            threadName = Thread.CurrentThread.Name;

            while (true)
            {
                Thread.Sleep(random.Next(1001));

                lock (this)
                {
                    while (suspended)
                    {
                        Monitor.Wait(this);
                    }
                }

                //获取随机的26个大写字母中的一个
                char displayChar = (Char)(random.Next(26) + 65);

                //Invoke 回到能够控制 GUI控件的线程中,执行委托的方法,并将相应的参数传递过去
                output.Invoke(new DisplayDelegate(DisplayChar),displayChar);


            }

        }

        public void Toggle()
        {
            suspended = !suspended;
            output.BackColor = suspended ? Color.Red : Color.LightGreen;

            lock (this)
            {
                if (!suspended)
                    Monitor.PulseAll(this);

                if (txtBoxShow != null)
                    txtBoxShow.Text = string.Format("线程:{0},控制状态由{1}——>{2}。", Thread.CurrentThread.ManagedThreadId, !suspended, suspended) + "
" + txtBoxShow.Text;
            }
        }

        private TextBox txtBoxShow;
        public RandomLetters(Label lbl,TextBox txtBox)
        {
            this.output = lbl;
            this.txtBoxShow = txtBox;
        }
    }
    #endregion
GUI与线程同步

实现效果如下:

六、死锁

* 死锁的几种情况:
* 1、互等死:线程之间互相引用,导致都不能释放
* 2、等到死:线程使用Wait后,再没有其它线程使用Pulse或PulseAll进行唤醒
* 3、异常死:在某个线程占用锁后,发生了异常,没有执行Exit释放锁操作,导致死锁
* 4、饿    死:一直有优先级高的线程加入,优先级低的线程始终没分配到处理器进行处理,这种无限延期称为饿死

原文地址:https://www.cnblogs.com/qiupiaohujie/p/11959162.html