複数のスレッドからアクセスされるクラスのインスタンスがあります。このクラスはこの呼び出しを受け取り、タプルをデータベースに追加します。データベースの制約により、並列スレッドによってデータベースの一貫性が失われる可能性があるため、これをシリアルに行う必要があります。
私は C# での並列処理と同時実行が初めてなので、次のようにしました。
private BlockingCollection<Task> _tasks = new BlockingCollection<Task>();
public void AddDData(string info)
{
Task t = new Task(() => { InsertDataIntoBase(info); });
_tasks.Add(t);
}
private void InsertWorker()
{
Task.Factory.StartNew(() =>
{
while (!_tasks.IsCompleted)
{
Task t;
if (_tasks.TryTake(out t))
{
t.Start();
t.Wait();
}
}
});
}
これAddDData
は、複数のスレッドによって呼び出されるスレッドでInsertDataIntoBase
あり、数ミリ秒かかる非常に単純な挿入です。
問題は、なんらかの理由で、私の知識不足で理解できないことです。タスクが 2 回呼び出されることがあります。常に次のようになります。
T1 T2 T3 T1 ← PK エラー。T4 ...
私は.Take()
完全に間違っていることを理解しましたか、何かが欠けているのでしょうか、それともプロデューサー/コンシューマーの実装が本当に悪いのでしょうか?
よろしく、 ラファエル
アップデート:
示唆されたように、私はこのアーキテクチャで簡単なサンドボックス テストの実装を作成しました。私が疑っていたように、前のタスクが終了する前にタスクが起動されないことを保証するものではありません。
問題は残ります: タスクを適切にキューに入れ、順番に起動するにはどうすればよいでしょうか?
更新 2:
コードを簡略化しました:
private BlockingCollection<Data> _tasks = new BlockingCollection<Data>();
public void AddDData(Data info)
{
_tasks.Add(info);
}
private void InsertWorker()
{
Task.Factory.StartNew(() =>
{
while (!_tasks.IsCompleted)
{
Data info;
if (_tasks.TryTake(out info))
{
InsertIntoDB(info);
}
}
});
}
同期された InsertIntoDB 呼び出しに依存しているため (ループ内にあるため)、Tasks を削除したことに注意してください。列。しかし、何度やっても、同じオブジェクトが 2 回使用されることがあります。