5

コードのブロックを排他的に実行する必要があるメソッドがありますが、本当に必要な場合にのみこの制限を追加したいと考えています。Id 値 (Int32) によっては、個別のオブジェクトをロード/変更することになるため、すべてのスレッドのアクセスをロックしても意味がありません。これを行う最初の試みは次のとおりです-

private static readonly ConcurrentDictionary<int, Object> LockObjects = new ConcurrentDictionary<int, Object>();
void Method(int Id)
{
    lock(LockObjects.GetOrAdd(Id,new Object())
    {
       //Do the long running task here - db fetches, changes etc
       Object Ref;
       LockObjects.TryRemove(Id,out Ref);
    }

}

これが機能するかどうかは疑問です-TryRemoveが失敗する可能性があります(これにより、ConcurrentDictionaryが大きくなり続けます)。

より明白なバグは、TryRemove がオブジェクトを正常に削除することですが、このオブジェクトを待機している (ロックアウトされている) (同じ ID の) 他のスレッドがある場合、同じ ID を持つ新しいスレッドが入ってきて、新しいオブジェクトを追加することです。追加したばかりのオブジェクトを待っている人が他にいないため、オブジェクトが処理を開始します。

代わりに、TPL またはある種の ConcurrentQueue を使用してタスクをキューに入れる必要がありますか? 最も簡単な解決策は何ですか?

4

4 に答える 4

3

ブランケット リソース ロックではなく、関連するアイテムのリソースをロックするために、同様のアプローチを使用しています...完全に機能します。

ほとんどそこにありますが、辞書からオブジェクトを削除する必要はありません。そのIDを持つ次のオブジェクトにオブジェクトのロックを取得させてください。

アプリケーション内の一意の ID の数に制限がありますか? その限界は何ですか?

于 2013-05-16T12:18:29.543 に答える
1

私が見る主なセマンティックの問題は、ロックの最後の行でオブジェクトが削除され、待機中のスレッドがそれを取得してロックできるため、コレクションにリストされていなくてもオブジェクトをロックできることです。

コレクションを、ロックを保護するオブジェクトのコレクションに変更します。オブジェクトが不要になると予想される場合を除き、名前を付けたり、コレクションからオブジェクトを削除したりしないでくださいLockedObjects

私は常に、このタイプのオブジェクトを、ロックまたはブロックされたオブジェクトではなく鍵と考えています。オブジェクトはロックされていません。ロックされたコード シーケンスへの鍵です。

于 2013-05-16T12:27:03.353 に答える
0

ID 自体を使用し、ハッシュ コードによって引き起こされる衝突を許可しない場合は、次の方法を使用できます。オブジェクトのディクショナリを維持し、ID を使用するスレッドの数に関する情報を保存します。

class ThreadLockerByID<T>
{
    Dictionary<T, lockerObject<T>> lockers = new Dictionary<T, lockerObject<T>>();

    public IDisposable AcquireLock(T ID)
    {
        lockerObject<T> locker;
        lock (lockers)
        {
            if (lockers.ContainsKey(ID))
            {
                locker = lockers[ID];
            }
            else
            {
                locker = new lockerObject<T>(this, ID);
                lockers.Add(ID, locker);
            }
            locker.counter++;
        }
        Monitor.Enter(locker);
        return locker;
    }
    protected void ReleaseLock(T ID)
    {
        lock (lockers)
        {
            if (!lockers.ContainsKey(ID))
                return;

            var locker = lockers[ID];

            locker.counter--;

            if (Monitor.IsEntered(locker))
                Monitor.Exit(locker);

            if (locker.counter == 0)
                lockers.Remove(locker.id);
        }
    }

    class lockerObject<T> : IDisposable
    {
        readonly ThreadLockerByID<T> parent;
        internal readonly T id;
        internal int counter = 0;
        public lockerObject(ThreadLockerByID<T> Parent, T ID)
        {
            parent = Parent;
            id = ID;
        }
        public void Dispose()
        {
            parent.ReleaseLock(id);
        }
    }
}

使用法:

partial class Program
{
    static ThreadLockerByID<int> locker = new ThreadLockerByID<int>();
    static void Main(string[] args)
    {
        var id = 10;
        using(locker.AcquireLock(id))
        {

        }
    }
}
于 2018-08-10T07:27:37.163 に答える