このコードが安全でない理由がわかりません。安全ではないことを証明するテストケースがあります。
List<T> _l = new List<T>();
public void Add(T t)
{
lock (_l)
{
_l.Add(t);
Monitor.PulseAll(_l);
}
}
public T[] RemoveToArray(TimeSpan timeout)
{
lock (_l)
{
if (_l.Count == 0)
{
bool timedout = !Monitor.Wait(_l, timeout);
// no lock
if (timedout)
{
return new T[0];
}
}
// with lock
T[] items = _l.ToArray();
_l.Clear();
return items;
}
}
この関数は、新しいアイテムが到着するまで、ある程度の時間 (その後タイムアウト) 待機することになっています。時間切れの場合は空の配列を返し、それ以外の場合は内部リストのすべての要素を配列に排出します。テスト コードでは、追加するタスクと削除するタスクの 2 つのタスクを作成しました。
times = 3;
Task[] tasks = new Task[2];
tasks[0] = Task.Factory.StartNew(() =>
{
firstRemoveItems = _l.RemoveToArray(_infinite);
Util.Delay(TimeSpan.FromMilliseconds(10)).Wait();
secondRemoveItems = _l.RemoveToArray(_infinite);
});
tasks[1] = Task.Factory.StartNew(() =>
{
_l.Add(new object());
Util.Delay(TimeSpan.FromMilliseconds(5)).Wait();
for (int i = 1; i < times; i++)
_l.Add(new object());
});
アイテムが 3 つ追加されましたが、合計アイテム数は 2、3、4 でした。何が問題なのかよくわかりません。待機タイムアウト後にロックが再取得されると思いますか?