C# 并行编程 之 互斥锁的使用
2015-05-15 10:07
417 查看
基本的使用
在并行编程中,访问临界区是经常会遇到的问题,加锁,释放锁是经常会使用到的解决方式。例如:
lock(_OneObject) { Do something with _OneObject ... }
lock会调用System.Threading.Monitor.Enter方法,然后通过 System.Threading.Monitor.Exit来进行释放。
上面的代码等同与
bool lockToken = false; try { Monitor.Enter(_OneObject, ref lockToken); Do something with _OneObject ... } finally { if (lockToken) Monitor.Exit(_OneObject); }
比较来看使用lock 代码简洁,而且不用担心Monito.Exit()的释放问题,但Monitor提供了更为丰富的锁控制接口。可以依据实际情况进行选择使用。
代码示例:
using System; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Sample5_2_monitor_lock { class Program { private static int _TaskNum = 3; private static Task[] _Tasks; private static StringBuilder _StrBlder; private const int RUN_LOOP = 50; private static void Work1(int TaskID) { int i = 0; string log = ""; bool lockToken = false; while (i < RUN_LOOP) { log = String.Format("Time: {0} Task : #{1} Value: {2} =====\n", DateTime.Now.TimeOfDay, TaskID, i); i++; try { lockToken = false; Monitor.Enter(_StrBlder, ref lockToken); _StrBlder.Append(log); } finally { if (lockToken) Monitor.Exit(_StrBlder); } } } private static void Work2(int TaskID) { int i = 0; string log = ""; bool lockToken = false; while (i < RUN_LOOP) { log = String.Format("Time: {0} Task : #{1} Value: {2} *****\n", DateTime.Now.TimeOfDay, TaskID, i); i++; try { lockToken = false; Monitor.Enter(_StrBlder, ref lockToken); _StrBlder.Append(log); } finally { if (lockToken) Monitor.Exit(_StrBlder); } } } private static void Work3(int TaskID) { int i = 0; string log = ""; bool lockToken = false; while (i < RUN_LOOP) { log = String.Format("Time: {0} Task : #{1} Value: {2} ~~~~~\n", DateTime.Now.TimeOfDay, TaskID, i); i++; try { lockToken = false; Monitor.Enter(_StrBlder, ref lockToken); _StrBlder.Append(log); } finally { if (lockToken) Monitor.Exit(_StrBlder); } } } static void Main(string[] args) { _Tasks = new Task[_TaskNum]; _StrBlder = new StringBuilder(); _Tasks[0] = Task.Factory.StartNew((num) => { var taskid = (int)num; Work1(taskid); }, 0); _Tasks[1] = Task.Factory.StartNew((num) => { var taskid = (int)num; Work2(taskid); }, 1); _Tasks[2] = Task.Factory.StartNew((num) => { var taskid = (int)num; Work3(taskid); }, 2); var finalTask = Task.Factory.ContinueWhenAll(_Tasks, (tasks) => { Task.WaitAll(_Tasks); Console.WriteLine("=========================================================="); Console.WriteLine("All Phase is completed"); Console.WriteLine("=========================================================="); Console.WriteLine(_StrBlder); }); try { finalTask.Wait(); } catch (AggregateException aex) { Console.WriteLine("Task failed And Canceled" + aex.ToString()); } finally { } Console.ReadLine(); } } }
测试结果当然是按照我们预想的,非常完美,这里就忽略了。
但可以看看如果没有锁的测试结果,虽然每个线程中循环才50次,就已经出现乱码了。
锁超时的使用
其中主要使用的是 Monitor.TryEnter(),函数,其中多了一个设置超时时间的参数。代码中让每个锁的超时Timer为2秒,在Work1中挺顿5秒,这样造成了Work2和Work3的超时。
代码示例:
using System; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Sample5_3_monitor_lock_timeout { class Program { private static int _TaskNum = 3; private static Task[] _Tasks; private static StringBuilder _StrBlder; private const int RUN_LOOP = 50; private static void Work1(int TaskID) { int i = 0; string log = ""; bool lockToken = false; while (i < RUN_LOOP) { log = String.Format("Time: {0} Task : #{1} Value: {2} =====\n", DateTime.Now.TimeOfDay, TaskID, i); i++; try { lockToken = false; Monitor.TryEnter(_StrBlder, 2000, ref lockToken); if (!lockToken) { Console.WriteLine("Work1 TIMEOUT!! Will throw Exception"); throw new TimeoutException("Work1 TIMEOUT!!"); } System.Threading.Thread.Sleep(5000); _StrBlder.Append(log); } finally { if (lockToken) Monitor.Exit(_StrBlder); } } } private static void Work2(int TaskID) { int i = 0; string log = ""; bool lockToken = false; while (i < RUN_LOOP) { log = String.Format("Time: {0} Task : #{1} Value: {2} *****\n", DateTime.Now.TimeOfDay, TaskID, i); i++; try { lockToken = false; Monitor.TryEnter(_StrBlder, 2000, ref lockToken); if (!lockToken) { Console.WriteLine("Work2 TIMEOUT!! Will throw Exception"); throw new TimeoutException("Work2 TIMEOUT!!"); } _StrBlder.Append(log); } finally { if (lockToken) Monitor.Exit(_StrBlder); } } } private static void Work3(int TaskID) { int i = 0; string log = ""; bool lockToken = false; while (i < RUN_LOOP) { log = String.Format("Time: {0} Task : #{1} Value: {2} ~~~~~\n", DateTime.Now.TimeOfDay, TaskID, i); i++; try { lockToken = false; Monitor.TryEnter(_StrBlder, 2000, ref lockToken); if (!lockToken) { Console.WriteLine("Work3 TIMEOUT!! Will throw Exception"); throw new TimeoutException("Work3 TIMEOUT!!"); } _StrBlder.Append(log); } finally { if (lockToken) Monitor.Exit(_StrBlder); } } } static void Main(string[] args) { _Tasks = new Task[_TaskNum]; _StrBlder = new StringBuilder(); _Tasks[0] = Task.Factory.StartNew((num) => { var taskid = (int)num; Work1(taskid); }, 0); _Tasks[1] = Task.Factory.StartNew((num) => { var taskid = (int)num; Work2(taskid); }, 1); _Tasks[2] = Task.Factory.StartNew((num) => { var taskid = (int)num; Work3(taskid); }, 2); var finalTask = Task.Factory.ContinueWhenAll(_Tasks, (tasks) => { Task.WaitAll(_Tasks); Console.WriteLine("=========================================================="); Console.WriteLine("All Phase is completed"); Console.WriteLine("=========================================================="); Console.WriteLine(_StrBlder); }); try { finalTask.Wait(); } catch (AggregateException aex) { Console.WriteLine("Task failed And Canceled" + aex.ToString()); } finally { } Console.ReadLine(); } } }
相关文章推荐
- C# 多线程编程之锁的使用【互斥锁(lock)和读写锁(ReaderWriterLock )】
- 5天玩转C#并行和多线程编程 —— 第三天 认识和使用Task
- 5天玩转C#并行和多线程编程 —— 第三天 认识和使用Task
- 5天玩转C#并行和多线程编程 —— 第三天 认识和使用Task
- C# 多线程编程之锁的使用【互斥锁(lock)和读写锁(ReadWriteLock)】
- C# 并行编程 之 Barrier的使用 - 通过屏障同步并发任务
- C# 并行编程 之 自旋锁的使用
- C# 并行编程 之 轻量级手动重置事件的使用
- C# 并行编程 之 限制资源的并发访问 使用SemaphoreSlim
- C# 并行编程 之 PLINQ 基本使用
- 使用C#和.NET 4编写的并行应用程序“多核并发编程的规则”
- 5天玩转C#并行和多线程编程 —— 第三天 认识和使用Task
- 5天玩转C#并行和多线程编程 —— 第三天 认识和使用Task
- C# 并行编程 之 Barrier的使用 - 通过屏障同步并发任务
- 5天玩转C#并行和多线程编程 —— 第三天 认识和使用Task
- C# 并行编程 之 Barrier的使用 - 通过屏障同步并发任务
- C# 并行编程 之 PLINQ并行度的指定 和 ForAll的使用
- C# 并行编程 之 ThreadPool的基本使用
- (转自博客园-雲霏霏)5天玩转C#并行和多线程编程 —— 第三天 认识和使用Task
- C# 并行编程 之 轻量级手动重置事件的使用