无关线程:线程之间没有任何联系,独立运行,互不干扰
相关线程:线程之间有联系,两个线程之间资源共享
临界线程:多个线程共享资源
临界区:访问临界资源代码
同步:两个线程协同工作才能完成同一项任务
相关线程实例:
public static char buffer; public static string str; static void Main(string[] args) { // 开写线程 Thread th = new Thread(write); th.Start(); // 开度线程 Thread re = new Thread(delegate() { for (int i = 0; i < str.Length; i++) { char ch = buffer; Console.Write(ch); // 醒木非根,半风走一,谈疏君莫,是一说人人人人人人人人人人人人人 Thread.Sleep(50); } }); re.Start(); Console.ReadLine(); } /// <summary> /// 写入 /// </summary> public static void write() { str = "醒木本非同根生,半生风雨走一程,谈笑疏狂君莫问,却是一位说书人"; for (int i = 0; i < str.Length; i++) { buffer = str[i]; Thread.Sleep(30); } }
打印结果:
醒木非根,半风走一,谈疏君莫,是一说人人人人人人人人人人人人人
造成这种情况的原因是:读写线程时间不相同。
Interlocked(互锁):
使用线程锁Interlocked来解决这个问题
Interlocked的一些属性
Interlocked.Increment(ref value) 数值加一(原子性操作)
Interlocked.Decrement(ref value) 数值减一(原子性操作)
Interlocked.Exchange(ref value1, value2) 交换:把值2赋给值1;返回新值
Interlocked.CompareExchange(ref value1, value2, value3) 实现比较和交换两种功能:值1和值3比较,如果相同,把值2给值1,不相同则不作任何操作;返回原值(多用于判断条件)
Interlocked.Read读取计数器的值
Interlocked.Add使计数器增加指定的值
使用Interlocked改造完成之后的代码:
// 开写线程 Thread th = new Thread(write); th.Start(); // 开度线程 Thread re = new Thread(delegate() { for (int i = 0; i < str.Length; i++) { while (Interlocked.Read(ref num) == 0) { Thread.Sleep(10); } char ch = buffer; Console.Write(ch); //Thread.Sleep(50); // 使值减少1 Interlocked.Decrement(ref num); } }); re.Start(); Console.ReadLine(); } /// <summary> /// 写入 /// </summary> public static void write() { str = "醒木本非同根生,半生风雨走一程,谈笑疏狂君莫问,却是一位说书人"; for (int i = 0; i < str.Length; i++) { while (Interlocked.Read(ref num) == 1) { Thread.Sleep(10); } buffer = str[i]; //Thread.Sleep(30); // 使值增加1 Interlocked.Increment(ref num); } }
Monitor(管程):
配合try-catch-finally(需要退出exit)或者Lock使用
锁定的对象应该声明为private static object obj = new object();尽量别用公共变量和字符串、this、值类型。
属性和方法:
Enter(Object) 在指定对象上获取排他锁。
Exit(Object) 释放指定对象上的排他锁。
IsEntered 确定当前线程是否保留指定对象锁。
Pulse 通知等待队列中的线程锁定对象状态的更改。
PulseAll 通知所有的等待线程对象状态的更改。
TryEnter(Object) 试图获取指定对象的排他锁。
TryEnter(Object, Boolean) 尝试获取指定对象上的排他锁,并自动设置一个值,指示是否得到了该锁。
Wait(Object) 释放对象上的锁并阻止当前线程,直到它重新获取该锁。
public static char buffer; public static string str; /// <summary> /// 此变量为InterLocked使用 /// </summary> public static long num; /// <summary> /// 此变量为Monitor 使用 /// </summary> public static object obj = new object(); static void Main(string[] args) { // 开写线程 Thread th = new Thread(write); th.Start(); // 开度线程 Thread re = new Thread(delegate() { try { for (int i = 0; i < str.Length; i++) { /*while (Interlocked.Read(ref num) == 0) { Thread.Sleep(10); }//*/ // 程序进入临界区,上锁 Monitor.Enter(obj); char ch = buffer; Console.Write(ch); // 通知等待队列中的线程锁定对象状态的更改 Monitor.Pulse(obj); // 释放对象上的锁并阻止当前线程,直到它重新获取该锁 Monitor.Wait(obj); /*//Thread.Sleep(50); // 使值减少1 Interlocked.Decrement(ref num);//*/ } } catch (Exception) { throw; } finally { Monitor.Exit(obj); } }); re.Start(); Console.ReadLine(); } /// <summary> /// 写入 /// </summary> public static void write() { try { str = "醒木本非同根生,半生风雨走一程,谈笑疏狂君莫问,却是一位说书人"; for (int i = 0; i < str.Length; i++) { /*while (Interlocked.Read(ref num) == 1) { Thread.Sleep(10); }//*/ // 程序进入临界区,上锁 Monitor.Enter(obj); buffer = str[i]; // 通知等待队列中的线程锁定对象状态的更改 Monitor.Pulse(obj); // 释放对象上的锁并阻止当前线程,直到它重新获取该锁 Monitor.Wait(obj); /*//Thread.Sleep(30); // 使值增加1 Interlocked.Increment(ref num);//*/ } } catch (Exception) { throw; } finally { Monitor.Exit(obj); } }
Lock:上锁
Monitor和Lock的区别
1.Lock是Monitor的语法糖。
2.Lock只能针对引用类型加锁。
3.Monitor能够对值类型进行加锁,实质上是Monitor.Enter(object)时对值类型装箱。
4.Monitor还有其他的一些功能。
使用lock代替try之后:程序变得简洁了一些:仅限于Monitor
static void Main(string[] args) { // 开写线程 Thread th = new Thread(write); th.Start(); // 开度线程 Thread re = new Thread(delegate() { for (int i = 0; i < str.Length; i++) { lock (obj) { /*while (Interlocked.Read(ref num) == 0) { Thread.Sleep(10); }//*/ char ch = buffer; Console.Write(ch); // 通知等待队列中的线程锁定对象状态的更改 Monitor.Pulse(obj); // 释放对象上的锁并阻止当前线程,直到它重新获取该锁 Monitor.Wait(obj); /*//Thread.Sleep(50); // 使值减少1 Interlocked.Decrement(ref num);//*/ } } }); re.Start(); Console.ReadLine(); } /// <summary> /// 写入 /// </summary> public static void write() { str = "醒木本非同根生,半生风雨走一程,谈笑疏狂君莫问,却是一位说书人"; for (int i = 0; i < str.Length; i++) { lock(obj) { /*while (Interlocked.Read(ref num) == 1) { Thread.Sleep(10); }//*/ buffer = str[i]; // 通知等待队列中的线程锁定对象状态的更改 Monitor.Pulse(obj); // 释放对象上的锁并阻止当前线程,直到它重新获取该锁 Monitor.Wait(obj); /*//Thread.Sleep(30); // 使值增加1 Interlocked.Increment(ref num);//*/ } } }
以上方法对资源消耗比较大,合理使用