1

LINQ to SQL でバックアップされたキューを作成したいと考えています。しかし、私はそれをどのように行うのが最善の方法なのかわかりません。

今、私は次のようなことをしました:

public static void Queue(Item item)
{
    var db = new MyDataContext();

    item.Time = DateTime.Now;

    db.Items.InsertOnSubmit(item);

    db.SubmitChanges();
}

public static Item TryDequeue()
{
    try
    {
        var db = new MyDataContext();

        var item = db.Items
            .Where(x => x.Status == 0)
            .OrderBy(x => x.Time)
            .FirstOrDefault();

        if (item == null)
            return null;

        item.Status += 1;

        db.SubmitChanges();

        return item;
    }
    catch (ChangeConflictException)
    {
        return null;
    }
}

しかし、私はいくつかChangeConflictExceptionのsを取得します。

結果のクエリが、要素を選択してステータスを設定し、競合なしで返すアトミック トランザクションになることを期待していましたが、そうではないようです。使用してみましTransactionScopeたが、デッドロックについて不平を言います。

これを達成するための最善の方法は何ですか?

4

3 に答える 3

2

これを行うことで、並行性の問題を続行できます

db.SubmitChanges(ConflictMode.ContinueOnConflict);

しかし、その落とし穴を理解してください。

于 2012-11-01T10:36:00.527 に答える
1

TryDequeueこれは、複数のスレッドで同時に実行しているように聞こえます。

キュー管理を 1 つのマスター スレッドに任せて、他のユーザーが利用可能になったときにワークアウトを渡すことを考えたことはありますか? 複数のスレッドが同じデータベース レコードを更新すると、常に問題が発生します。

トランザクション内のすべてを強制したとしても、スレッドはブロックを開始し、他のスレッドがワーク キューをチェックするのを待ちます (マルチスレッドの観点から、データベース操作は低速です)。これはすぐにボトルネックになります。

別の方法は、スレッドセーフなコレクションにメモリ内キャッシュを配置することです。キュー項目はコレクションにロードされ、スレッドはそこからデキューできます。頻繁に (またはキューが空の場合)、メソッドを介した最初のスレッドがブロックされ、キャッシュ内の変更がデータベースにフラッシュされ、新しいキューが読み込まれ、通常どおりデキューされ、ブロックが停止する可能性があります。これにより、データベースのトリップ数が大幅に削減され、常に単一のスレッドのみが更新を実行するため、競合の問題が回避されます。もちろん、欠点は、ジョブの状態が更新されてからその変更がデータベースに保存されるまでに遅延が発生することです。これがどの程度の問題になるかによって異なります。

現在のエラーを解決しますが、根本的な問題は解決しない迅速で汚い修正はlock、 Dequeue メソッドを介して移動するスレッドを制限することです...

private Object dequeueLock = new Object();
public static Item TryDequeue()
{
    lock (dequeueLock)
    {
        try
        {
            var db = new MyDataContext();

            var item = db.Items
                .Where(x => x.Status == 0)
                .OrderBy(x => x.Time)
                .FirstOrDefault();

            if (item == null)
                return null;

            item.Status += 1;

            db.SubmitChanges();

            return item;
        }
        catch (ChangeConflictException)
        {
            return null;
        }
    }
}
于 2012-11-01T11:14:39.543 に答える
-1

これを試して...

private static readonly object _lock = new object

public static void Queue(Item item)
{
    try
    {
        Monitor.Enter(_lock);
        var db = new MyDataContext();

        item.Time = DateTime.Now;

        db.Items.InsertOnSubmit(item);

        db.SubmitChanges();
    }
    finally
    {
        Monitor.Exit(_lock);
    }
}

public static Item TryDequeue()
{
    try
    {
        Monitor.Enter(_lock);
        var db = new MyDataContext();

        var item = db.Items
            .Where(x => x.Status == 0)
            .OrderBy(x => x.Time)
            .FirstOrDefault();

        if (item == null)
            return null;

        item.Status += 1;

        db.SubmitChanges();

        return item;
    }
    catch (ChangeConflictException)
    {
        return null;
    }
    finally
    {
        Monitor.Exit(_lock);
    }
}
于 2012-11-01T10:25:31.233 に答える