• 多线程状态与优先级、线程同步与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、饿    死:一直有优先级高的线程加入,优先级低的线程始终没分配到处理器进行处理,这种无限延期称为饿死

  • 相关阅读:
    Linux下c开发 之 线程通信(转)
    mount -t nfs 的使用
    window共享linux下的文件 samba
    C/C++ 的使用
    php获取格式时间和时间戳
    php压缩文件夹
    php递归删除文件夹
    php生成文件夹(递归生成)
    QQ音乐API分析记录
    $(this)与this的区别
  • 原文地址:https://www.cnblogs.com/qiupiaohujie/p/11959162.html
Copyright © 2020-2023  润新知