この質問は何度も要求されていますが、これは特別なケースです。ロックを使用してもコレクションは変更されません。列挙操作が実行されない可能性があります
public class JobStatusMonitor
{
private static List<Job> _runningJobs = new List<Job>();
private static object myLock = new object();
public static void AddJob(GPSJob input)
{
lock (myLock)
_runningJobs.Add(input);
}
public static void Start(int pollInterval)
{
while (true)
{
var removeJobs = new List<GPSJob>();
lock (myLock)
{
foreach (var job in _runningJobs)
{
if (job.IsComplete())
{
removeJobs.Add(job);
}
}
}
foreach (var job in removeJobs)
{
_runningJobs.Remove(job);
}
System.Threading.Thread.Sleep(pollInterval);
}
}
}
リスト_runningJobsはプライベートなので、AddJobメソッドを使用しない限り、このクラスの外には変更できません。 AddJobメソッドはforeachループと同じロックを使用するため、反復処理中はコレクションを変更できません。
何が起きているのか分かっていれば、Start(5000)が呼び出されます。リストに何もないので、Thread.Sleep()にスキップします。バックグラウンドプロセスはジョブをリストに追加します。 whileループはforeachループに戻り、ロックを適用します。リストを反復処理する間、コレクションに追加しようとする他のスレッドは反復が完了するまで待機します。反復処理が完了すると、各スレッドはジョブを追加します。ジョブを追加しようとするスレッドが多数ある場合でも、ロックによって競合条件は発生しません。
実際には、このスレッドがスリープしている間に追加されたジョブはすべて正常に追加されます。このリストが反復処理されている間に追加されたジョブは、ロックされていても反復処理が完了するまで待機しません。
なぜこのエラーを防ぐロックはありませんか?
EDIT:ロック内の新しいリストにコピーすると、エラーが解消されます。
public class JobStatusMonitor
{
private static List<Job> _runningJobs = new List<Job>();
private static object myLock = new object();
public static void AddJob(GPSJob input)
{
lock (myLock)
{
_runningJobs.Add(input);
}
}
public static void Start(int pollInterval)
{
while (true)
{
lock (myLock)
{
var completeJobs = _runningJobs.Where(job => job.IsComplete()).ToList();
foreach (var job in completeJobs)
{
_runningJobs.Remove(job);
job.TaskCompletionSource.SetResult(null);
}
}
System.Threading.Thread.Sleep(pollInterval);
}
}
}
最後の 'foreach'は、ロックの外側でコレクションを変更していることに注意してください。 –
反復処理中にジョブが追加されたことをどう知っていますか?あなたはこれの証拠を持っていますか?なぜロックの外にRemove()があるのですか? – Eli
キューをシミュレートするためにリストを使用しているようです。なぜ、列挙しようとしているのではなく、 'while'ループで[' ConcurrentQueue'](https://msdn.microsoft.com/en-us/library/dd267265.aspx)(これはスレッドセーフです)他のスレッドがアイテムを追加しようとしている間にリストとブロックを行う? –