C#パラレルプログラミングの反発ロックの使用
基本的な使用
パラレルプログラミングでは、臨界領域へのアクセスはよく発生する問題であり、ロックを追加し、ロックを解放することはよく使用される解決策である.例:
ロックは、System.Threading.Monitor.Enterメソッドを呼び出し、System.Threading.Monitor.Exitによって解放します.上のコードは
比較するとlockコードの使用は簡潔で、Monito.Exit()の解放の問題を心配する必要はありませんが、Monitorはより豊富なロック制御インタフェースを提供しています.実際の状況に応じて選択して使用することができます.
コードの例:
テストの結果はもちろん私たちの予想通りで、とても完璧で、ここでは無視しました.
しかし、ロックされたテスト結果がなければ、スレッドごとに50回しかループしていないのに、文字化けしていることがわかります.
ロックタイムアウトの使用
主にMonitor.TryEnter()を使用し、タイムアウト時間を設定するパラメータが1つ増えています.コードでは、各ロックのタイムアウトTimerを2秒、ワーク1で5秒として実行し、ワーク2とワーク3のタイムアウトをもたらします.
コードの例:
パラレルプログラミングでは、臨界領域へのアクセスはよく発生する問題であり、ロックを追加し、ロックを解放することはよく使用される解決策である.例:
lock(_OneObject)
{
Do something with _OneObject ...
}
ロックは、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} =====
",
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} *****
",
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} ~~~~~
",
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()を使用し、タイムアウト時間を設定するパラメータが1つ増えています.コードでは、各ロックのタイムアウトTimerを2秒、ワーク1で5秒として実行し、ワーク2とワーク3のタイムアウトをもたらします.
コードの例:
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} =====
",
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} *****
",
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} ~~~~~
",
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();
}
}
}