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

时间:2023-03-09 03:13:59
多线程状态与优先级、线程同步与Monitor类、死锁

一、线程状态

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

二、线程优先级

多线程状态与优先级、线程同步与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("线程启动:\n"); t1.Start();
t2.Start();
t3.Start(); Console.WriteLine("线程结束:\n");
string r= Console.ReadLine();
if (r == "")
break;
}
}
} class MessagePrinter
{
private int _sleepTime;
private static Random _random=new Random(); public MessagePrinter()
{
_sleepTime = _random.Next();
} 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=-; 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 = -;
private int occupiedBufferCount = ; public int Buffer
{
get
{
//锁定当前对象
Monitor.Enter(this); try
{
//如果没有Buffer中没有更新数据,则进入线程等待
if (occupiedBufferCount == )
{
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, ))
{
Console.WriteLine("【set时,TryEnter失败,将使用Enter进入。】");
Monitor.Enter(this);
} if (occupiedBufferCount == )
{
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}\n", operation, _Buffer, occupiedBufferCount));
}
} /// <summary>
/// 当生产者与消费者速度基本同步时,适当增加缓冲区空间,从而减少互等时间。
/// 使用Lock代替Monitor.Enter和Exit方法
/// </summary>
public class CircularBuffer : IBuffer
{
private int[] _Buffer = {-,-,-};
private int occupiedBufferCount = ; private int readLocation = ;//当前读取位置
private int writeLocation = ;//当前写入位置 public int Buffer
{
get
{
Monitor.Enter(this);
try
{
if (occupiedBufferCount == )
{
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 + ) % _Buffer.Length;
Console.WriteLine(CreateStateOutput()); Monitor.Pulse(this);
return readValueCopy; }
catch (Exception ex)
{
Console.WriteLine(string.Format("异常:{0}",ex.Message.ToString()));
return -;
}
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 + ) % _Buffer.Length; Console.WriteLine(CreateStateOutput()); Monitor.Pulse(this);
}
} } private String CreateStateOutput()
{
string output = "(buffer occupied:" + occupiedBufferCount + ")\nbuffers:";
for (int i = ; i < _Buffer.Length; i++)
output += " " + string.Format("{0,2}", _Buffer[i]) + " "; output += "\n";
output += " "; for (int i = ; i < _Buffer.Length; i++)
output += "-----"; output += "\n";
output += " "; for (int i = ; 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 += "\n"; 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 = ; count <= ; count++)
{
Thread.Sleep(random.Next(, ));
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 = ;
for (int count = ; count <= ; count++)
{
Thread.Sleep(random.Next(, ));
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()); lock (this)
{
while (suspended)
{
Monitor.Wait(this);
}
} //获取随机的26个大写字母中的一个
char displayChar = (Char)(random.Next() + ); //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) + "\r\n" + txtBoxShow.Text;
}
} private TextBox txtBoxShow;
public RandomLetters(Label lbl,TextBox txtBox)
{
this.output = lbl;
this.txtBoxShow = txtBox;
}
}
#endregion

GUI与线程同步

实现效果如下:

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

六、死锁

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