【笔记】《C#大学教程》- 第14章 多线程

1.线程的生命周期:


(1).创建Thread,线程处于Unstarted状态,将一个ThreadStart委托作为其构造函数的参数,该委托必须是一个可以返回void类型数据,并不接受任何参数的方法,该委托指定了线程在运行中执行的动作。

(2).调用一个线程的Start方法使一个线程进入Started状态,系统为线程指定处理器后,优先级最高的线程便开始运行Running状态,并执行ThreadStart委托。

(3).当一个ThreadStart委托终止时,线程进入Stopped状态,如果该线程没有被引用,那么会被系统执行垃圾回收。此外,可以通过调用线程的Abort方法强制线程终止,并抛出ThreadAbortException异常。

(4).当线程发出IO输入输出请求时,那么线程在完成请求前会处于阻塞Blocked状态,当完成请求后便能返回Started状态继续等待执行。

(5).处于运行状态的线程可以通过三种方式进入休眠WaitSleepJoin状态:

<1>.调用Monitor类的Wait方法。通过调用调用Monitor类的Pulse或者PulseAll方法可以将线程恢复到Started状态;通过调用Pulse方法使下一条等待的线程回到Start状态,Pulse则是全部等待回到Start状态

<2>.调用线程的Sleep方法让线程休眠。当休眠时间到达以后线程恢复Started状态;

<3>.调用线程的Interrupt方法可以让上面两种进入WaitSleepJoin状态的线程返回Stared状态。

<4>.当一个线程调用另一个线程的Join方法时,该线程进入WaitSleepJoin状态,当另一个线程Stopped后改线程返回Stared状态。

(6).通过调用线程的Suspend和Resume方法挂起和继续线程;


2.线程的优先级:

通过Priority属性调整,该属性的枚举在ThreadPriority枚举类中定义。


3.举个栗子:

using System;
using System.Threading;

namespace ThreadTester
{
    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(
                current.Name + " going to sleep for " + sleepTime);

            Thread.Sleep(sleepTime);
            Console.WriteLine(current.Name + " done sleeping");
        }
    }
}

using System;
using System.Threading;

namespace ThreadTester
{
    class Program
    {
        static void Main(string[] args)
        {
            MessagePrinter printer1 = new MessagePrinter();
            Thread thread1 = new Thread(new ThreadStart(printer1.Print));
            thread1.Name = "thread1";

            MessagePrinter printer2 = new MessagePrinter();
            Thread thread2 = new Thread(new ThreadStart(printer2.Print));
            thread2.Name = "thread2";

            MessagePrinter printer3 = new MessagePrinter();
            Thread thread3 = new Thread(new ThreadStart(printer3.Print));
            thread3.Name = "thread3";

            Console.WriteLine("Starting threads");
            thread1.Start();
            thread2.Start();
            thread3.Start();
            Console.WriteLine("Threads started\n");
        }
    }
}

Starting threads
thread1 going to sleep for 3837
thread2 going to sleep for 4977
Threads started

thread3 going to sleep for 3063
thread3 done sleeping
thread1 done sleeping
thread2 done sleeping
请按任意键继续. . .

4.线程同步和类监视器:

为了防止多线程造成共享数据的异步读写问题,C#使用Monitor类来锁定对象,是一次只能有一个线程来访问该对象,以实现共享数据同步访问。

(1).调用Enter方法获得数据对象上的锁。每个对象都有一个SyncBlock,他保存了对象锁的状态。当对象被锁定时,所有申请获得锁的线程都会进入blocked状态,当被占用该对象的线程调用Monitor的Exit方法时,就会解锁对该数据对象的访问,SyncBlock便会更新,之前blocked视图获取该对象的某个线程就可以重新对其上锁。

(2).使用lock块来给对象上锁:当lock块终止时,便会释放对象上的锁。

lock(objRef)
{
}
(3).使用Monitor的Wait方法释放对象锁。


5.例子:生产者/消费者关系

using System.Windows.Forms;
using System.Threading;

namespace CircularBuffer
{
    class HoldIntegerSynchronized
    {
        //共享数据,缓冲区
        private int[] buffers = { -1, -1, -1 };
        //缓冲区有效数据长度
        private int occupiedBufferCount = 0;
        private TextBox outputTextBox;

        //读取位置以及写入位置
        private int readLocation = 0, writeLocation = 0;

        public HoldIntegerSynchronized(TextBox output)
        {
            outputTextBox = output;
        }

        public int Buffer
        {
            get
            {
                //等效于Monitor.Enter(this)开始和Exit(this)结束
                lock (this)
                {
                    //如果没有可以读取的有效数据,那么等待
                    if (occupiedBufferCount == 0)
                    {
                        outputTextBox.Text += "\r\nAll buffers empty. " +
                            Thread.CurrentThread.Name + " waits.";
                        outputTextBox.ScrollToCaret();
                        Monitor.Wait(this);
                    }

                    //如果有有效数据
                    int readValue = buffers[readLocation];

                    outputTextBox.Text += "\r\n" +
                        Thread.CurrentThread.Name + " reads " +
                        readValue + " ";

                    --occupiedBufferCount;

                    readLocation =
                        (readLocation + 1) % buffers.Length;
                    outputTextBox.Text += CreateStateOutput();
                    outputTextBox.ScrollToCaret();
                    //让下一个等待共享数据的线程恢复Started
                    Monitor.Pulse(this);

                    return readValue;
                }
            }

            set
            {
                lock (this)
                {
                    //如果缓冲区满了,那么不能继续写入,请等待
                    if (occupiedBufferCount == buffers.Length)
                    {
                        outputTextBox.Text += "\r\nAll buffers full. " +
                            Thread.CurrentThread.Name + " waits.";
                        outputTextBox.ScrollToCaret();
                        Monitor.Wait(this);
                    }

                    //如果缓冲区没有满,那么继续写入
                    buffers[writeLocation] = value;
                    outputTextBox.Text += "\r\n" +
                        Thread.CurrentThread.Name + " writes " +
                        value + " ";
                    ++occupiedBufferCount;
                    writeLocation =
                        (writeLocation + 1) % buffers.Length;
                    outputTextBox.Text += CreateStateOutput();
                    outputTextBox.ScrollToCaret();
                    //让下一个等待共享数据的线程恢复Started
                    Monitor.Pulse(this);
                }
            }
        }

        public string CreateStateOutput()
        {
            string output = "(buffers occupied: " +
                occupiedBufferCount + ")\r\nbuffers: ";

            for (int i = 0; i < buffers.Length; ++i)
            {
                output += " " + buffers[i] + " ";
            }

            output += "\r\n ";

            for (int i = 0; i < buffers.Length; i++)
            {
                output += "----";
            }

            output += "\r\n ";

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

            output += "\r\n";

            return output;
        }
    }
}

using System;
using System.Windows.Forms;
using System.Threading;

namespace CircularBuffer
{
    class Producer
    {
        private HoldIntegerSynchronized sharedLocation;
        private TextBox outputTextBox;
        private Random randomSleepTime;

        public Producer(HoldIntegerSynchronized shared, Random random, TextBox output)
        {
            sharedLocation = shared;
            randomSleepTime = random;
            outputTextBox = output;
        }

        public void Produce()
        {
            for (int count = 11; count <= 20; count++)
            {
                Thread.Sleep(randomSleepTime.Next(1, 3000));
                sharedLocation.Buffer = count;
            }

            string name = Thread.CurrentThread.Name;

            outputTextBox.Text += "\r\n" + name +
                " done producing.\r\n" + name + " terminated.\r\n";
            outputTextBox.ScrollToCaret();
        }
    }
}

using System;
using System.Windows.Forms;
using System.Threading;

namespace CircularBuffer
{
    class Consumer
    {
        private HoldIntegerSynchronized sharedLocation;
        private TextBox outputTextBox;
        private Random randomSleepTime;

        public Consumer(HoldIntegerSynchronized shared, Random random, TextBox output)
        {
            sharedLocation = shared;
            randomSleepTime = random;
            outputTextBox = output;
        }

        public void Consume()
        {
            int sum = 0;

            for (int count = 1; count <= 10; count++)
            {
                Thread.Sleep(randomSleepTime.Next(1, 3000));
                sum += sharedLocation.Buffer;
            }

            string name = Thread.CurrentThread.Name;

            outputTextBox.Text += "\r\nTotal" + name +
                " consumed:" + sum + ".\r\n" + name + 
                " terminated.\r\n";
            outputTextBox.ScrollToCaret();
        }
    }
}

<pre name="code" class="csharp">using System;
using System.Windows.Forms;
using System.Threading;

namespace CircularBuffer
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            HoldIntegerSynchronized sharedLocation = new HoldIntegerSynchronized(outputTextBox);
            outputTextBox.Text = sharedLocation.CreateStateOutput();
            Random random = new Random();
            Producer producer = new Producer(sharedLocation, random, outputTextBox);
            Consumer consumer = new Consumer(sharedLocation, random, outputTextBox);
            Thread producerThread = new Thread(new ThreadStart(producer.Produce));
            producerThread.Name = "Producer";
            Thread consumerThread = new Thread(new ThreadStart(consumer.Consume));
            consumerThread.Name = "Consumer";
            producerThread.Start();
            consumerThread.Start();
        }
    }
}
 
(buffers occupied: 0)
buffers:  -1  -1  -1 
         ------------
          WR     

Producer writes 11 (buffers occupied: 1)
buffers:  11  -1  -1 
         ------------
          R  W   

Producer writes 12 (buffers occupied: 2)
buffers:  11  12  -1 
         ------------
          R    W 

Producer writes 13 (buffers occupied: 3)
buffers:  11  12  13 
         ------------
          WR     

Consumer reads 11 (buffers occupied: 2)
buffers:  11  12  13 
         ------------
          W  R   

Consumer reads 12 (buffers occupied: 1)
buffers:  11  12  13 
         ------------
          W    R 

Producer writes 14 (buffers occupied: 2)
buffers:  14  12  13 
         ------------
            W  R 

Producer writes 15 (buffers occupied: 3)
buffers:  14  15  13 
         ------------
              WR 

All buffers full. Producer waits.
Consumer reads 13 (buffers occupied: 2)
buffers:  14  15  13 
         ------------
          R    W 

Producer writes 16 (buffers occupied: 3)
buffers:  14  15  16 
         ------------
          WR     

All buffers full. Producer waits.
Consumer reads 14 (buffers occupied: 2)
buffers:  14  15  16 
         ------------
          W  R   

Producer writes 17 (buffers occupied: 3)
buffers:  17  15  16 
         ------------
            WR   

All buffers full. Producer waits.
Consumer reads 15 (buffers occupied: 2)
buffers:  17  15  16 
         ------------
            W  R 

Producer writes 18 (buffers occupied: 3)
buffers:  17  18  16 
         ------------
              WR 

Consumer reads 16 (buffers occupied: 2)
buffers:  17  18  16 
         ------------
          R    W 

Consumer reads 17 (buffers occupied: 1)
buffers:  17  18  16 
         ------------
            R  W 

Producer writes 19 (buffers occupied: 2)
buffers:  17  18  19 
         ------------
          W  R   

Producer writes 20 (buffers occupied: 3)
buffers:  20  18  19 
         ------------
            WR   

Producer done producing.
Producer terminated.

Consumer reads 18 (buffers occupied: 2)
buffers:  20  18  19 
         ------------
            W  R 

Consumer reads 19 (buffers occupied: 1)
buffers:  20  18  19 
         ------------
          R  W   

Consumer reads 20 (buffers occupied: 0)
buffers:  20  18  19 
         ------------
            WR   

TotalConsumer consumed:155.
Consumer terminated.


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值