您的位置:首页 > 编程语言 > C#

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();
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: