多线程编程学习笔记-基础(三)
接上文 多线程编程学习笔记-基础(一)
接上文 多线程编程学习笔记-基础(二)
九、向线程传递参数
1.代码如下。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; //引入线程 using System.Diagnostics; namespace ThreadConsoleApp { class Program { static void Main(string[] args) { Console.WriteLine("开始,给线程传参数"); var fore = new ThreadBackground(10); Thread t = new Thread(fore.CountNumber); t.Name = "线程1"; //启动线程 t.Start(); t.Join(); Console.WriteLine("----------------------------"); var t2 = new Thread(Count); t2.Name = "线程2"; t2.Start(8); t2.Join(); Console.WriteLine("----------------------------"); var t3 = new Thread(()=>CountNumber(12)); t3.Name = "线程3"; t3.Start(); t3.Join(); Console.WriteLine("----------------------------"); int i = 10; var t4 = new Thread(() => PrintNumber(i)); t4.Name = "线程4"; i = 20; var t5 = new Thread(() => PrintNumber(i)); t5.Name = "线程5"; t4.Start(); t5.Start(); Console.Read(); } static void CountNumber(int cnt) { for (int i = 0; i < cnt; i++) { Thread.Sleep(500); Console.WriteLine(string.Format(" {0} 打印 {1,11} 数字", Thread.CurrentThread.Name, i.ToString("N0"))); } } static void Count(object cnt) { CountNumber((int)cnt); } static void PrintNumber(int num) { Console.WriteLine(string.Format(" {0} 打印 {1,11} 数字", Thread.CurrentThread.Name, num.ToString("N0"))); } } }
2.结果如下图。
线程1,我们通过实例化对象来进行参数传递。
线程2,我们使用Thread.Start()来传递参数,不过此方法只接收单个参数,而且是对象类型。
线程3,我们使用lambda表达式进行参数传递,lambda表达式定义了一个不属于任何类的方法,同时该方法调用了我们实际要执行的方法,同时传递参数给线程。
线程4与线程5,则是显示了使用lambda表达式进行参数传递的一个问题,即当多个lambda表达式共用一个变量时,它们会共享这个变量的值。如图中线程4与线程5所显示,没有打印10,只打印了20。
十、使用lock锁定操作
1.代码如下
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; //引入线程 using System.Diagnostics; namespace ThreadConsoleApp { class Program { static void Main(string[] args) { Console.WriteLine("开始,给线程加锁"); var c = new Counter(); Thread t = new Thread(()=>Count(c)); var t3 = new Thread(() => Count(c)); var t2 = new Thread(() => Count(c)); t.Name = "线程1"; //启动线程 t.Start(); t2.Name = "线程2"; t2.Start(); t3.Name = "线程3"; t3.Start(); t.Join(); t2.Join(); t3.Join(); Console.WriteLine(string.Format("没有加锁的多线程总计:{0}",c.Count)); Console.WriteLine("----------------------------"); var c1 = new CounterLock(); var t4 = new Thread(() => Count(c1)); t4.Name = "线程4"; var t5 = new Thread(() => Count(c1)); t5.Name = "线程5"; var t6 = new Thread(() => Count(c1)); t6.Name = "线程6"; t4.Start(); t5.Start(); t6.Start(); t4.Join(); t5.Join(); t6.Join(); Console.WriteLine(string.Format("加锁的多线程总计:{0}", c1.Count)); Console.Read(); } static void Count(CountBase cnt) { for (int i = 0; i < 100000; i++) { cnt.Incerement(); cnt.Dncerement(); } } } abstract class CountBase { public abstract void Incerement(); public abstract void Dncerement(); } class Counter : CountBase { public int Count { get; private set; } public override void Dncerement() { Count--; } public override void Incerement() { Count++; } } class CounterLock : CountBase { private readonly object objSync = new object(); public int Count { get; private set; } public override void Dncerement() { lock (objSync) { Count--; } } public override void Incerement() { lock (objSync) { Count++; } } } }
2. 结果如下图
主线程首先创建了一个 Counter的实例对象,这个类定义了一个可以增,可以 减的简单计数器。然后我们创建了三个线程,这三个线程共享一个Counter对象。由于没有对共享变量的锁定,所以在一个周期内,对共享变量的改变,在上个线程没结束之前,当前线程又对共享变量进行了操作,我们会得到不同的计数值,如上图所示。为了防止这种情况的发生,所以我们要对共享变量进行加锁。使用lock关键字锁定对象,这样在一个线程操作完成之前,其他线程都不能对共享变量进行操作。
十一、Moniter对资源的锁定
1.代码如下
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; //引入线程 using System.Diagnostics; namespace ThreadConsoleApp { class Program { static void Main(string[] args) { Console.WriteLine("开始,线程死锁"); var lock1 = new object(); var lock2 = new object(); Thread t = new Thread(()=> DeadLock(lock1,lock2)); t.Name = "线程1"; //启动线程 t.Start(); lock (lock2) { Thread.Sleep(2000); if (Monitor.TryEnter(lock1,TimeSpan.FromSeconds(5))) { Console.WriteLine("在规定时间内,请求资源"); } else { Console.WriteLine("超时,无法获取资源"); } } new Thread(() => DeadLock(lock1, lock2)).Start(); Console.WriteLine("-----------------------------"); lock (lock2) { Thread.Sleep(1000); Console.WriteLine(string.Format("死锁线程")); lock(lock1) { Console.WriteLine("请求资源成功"); } } Console.Read(); } /// <summary> /// 死锁方法 /// </summary> /// <param name="objLock1"></param> /// <param name="objLock2"></param> static void DeadLock(object objLock1,object objLock2) { lock (objLock1) { Thread.Sleep(2000); lock (objLock2) { Console.WriteLine("死锁"); } } } } }
2.结果如下图
先看deadlock方法,这个方法先锁定lock1对象,然后等待2秒之后,锁定了lock2对象。然后在子线程中启动了这个方法。
主线程中先锁定了lock2对象,然后等待获取lock1对象。由于子线程锁定了lock1对象,等待lock2对象。所以造成了死锁。
十二、多线程的异常处理
1.代码如下
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; //引入线程 using System.Diagnostics; namespace ThreadConsoleApp { class Program { static void Main(string[] args) { Console.WriteLine("开始,异常处理"); Thread t = new Thread(FaultyThread); t.Name = "线程1"; //启动线程 t.Start(); t.Join(); try { t = new Thread(ExpectThread); t.Start(); } catch (Exception ex) { Console.WriteLine("异常信息:" + ex.Message); } Console.Read(); } static void ExpectThread() { Console.WriteLine(string.Format("异常处理")); Thread.Sleep(2000); throw new Exception("抛出异常"); } static void FaultyThread() { try { Console.WriteLine(string.Format("异常处理2")); Thread.Sleep(1000); throw new Exception("抛出异常2"); } catch (Exception ex) { Console.WriteLine(string.Format("异常处理2:{0}",ex.Message)); } } } }
2.结果如下图。
在程序中定义了两个处理异常的方法,一个对异常进行了处理,另一个没有对异常进行处理。最后如图。程序崩溃了。