4

スレッドセーフ リストを実装したいのですが、単一の操作 (追加、削除など) だけでなく、操作のブロック全体でスレッド セーフを確保する必要があります。ユース ケースは次のようになります。

list.Lock();

list.Add(sth);
list.RemoveAt(4);

list.Unlock();

リストですべての操作にロックが必要になるようにします。例えば:

list.Add(sth);

事前にロックせずに呼び出すと、例外が発生するはずです。lock()これが、このステートメントを使用しない理由です。このソリューションでは、ロック チェックが重要です。

Monitor を使用してそのようなリストを実装するのは難しくありませんが、リストがロックされているかどうかを確認するまでは必要です。次のシナリオを考えました。

// Inside list class

private object lockObject;
private bool locked;

public void Lock()
{
    Monitor.Enter(lockObject);
    locked = true;
}

public void Unlock()
{
    Monitor.Exit(lockObject);
    locked = false;
}

locked残念ながら、このコードは、クリティカル セクションに入る前またはクリティカル セクションから出る前または後に設定されているかに関係なく、競合状態になりがちです。

TryEnter を使用する別の方法もありますが、この方法では、ロックが取得されない場合に実際にクリティカル セクションに入り、競合状態が発生する可能性があります。

リストがロックされているかどうかにかかわらず、このメカニズムをスレッドセーフにする方法と、チェック中に競合状態を回避する方法を実装する必要がありますか?

4

4 に答える 4

3

Builder パターンに似たものを考えています。

使用例:

list
    .do() // mandatory initial statement.
          // Doesn't acquire a lock - it just builds a transaction object

    .add(42) // Add an operation to the transaction object.
             // Call would be illegal without performing do() before

    .removeAt(0) // Another added operation

    .end(); // Acquire the lock, perform the specified changes, release the lock.

によって実行されるロックの取得はend()、単純な呼び出しであるsync {...}可能性があります - 競合状態の可能性はありません。

于 2013-06-11T06:58:27.373 に答える
3

クラスがロックを担当するようにし、消費者が指定する を受け入れる 1 つのメソッドのみを公開できます。Action

public class LockedCollection
{
    private class CollectionImpl : ICollection<int>
    {
       //Collection methods
    }

    private sealed class CollectionWrapper : ICollection<int>, IDisposable
    {
       public CollectionWrapper(CollectionImpl inner)
       {
           _inner = inner;
       }
       private CollectionImpl _inner
       //Collection methods all just wrapping calls to inner
       public void Dispose()
       {
          _inner = null;
       }
    }


    private CollectionImpl _instance - new CollectionImpl();
    private object _lock = new object();

    public void DoStuff(Action<ICollection<int>> task)
    {
        lock(_lock)
        {
            using(var wrapper = new CollectionWrapper(_instance))
            {
                task(wrapper);
            }
        }
    }
}

消費者は、内部taskで好きな操作のシーケンスを持つことができ、自分でロックを取得したため、ロックが取得されたことがわかります。

于 2013-06-11T07:02:10.950 に答える
1

ロックとして使用されるオブジェクトに getter を提供する場合は、標準のロック メカニズムを使用できます。これがエレガントな解決策だとは思いませんが、確かにうまくいくでしょう。

void performOpertations(TSList list) {
  lock(list.getLock()) {
    list.add(obj);
    list.remove(obj2);
    // do your stuff
  }
}

add()C# の同期ブロックは再入可能であるため、または例のように、内部的に同期されたリストのメソッドを呼び出すことができますremove()

もう 1 つの解決策は、ビジター パターンを使用し、ビジター アクションを同期ブロックで囲むことです。

于 2013-06-11T07:02:27.560 に答える
1

ReaderWriterLockSlimを使用しない理由がわかりません:

class MyThreadSafeList<T>
{
    private readonly List<T> internalList = new List<T>();
    private readonly ReaderWriterLockSlim lockSlim = new ReaderWriterLockSlim();

    public void EnterReadLock()
    {
        lockSlim.EnterReadLock();
    }

    public void ExitReadLock()
    {
        lockSlim.ExitReadLock();
    }

    public void EnterWriteLock()
    {
        lockSlim.EnterWriteLock();
    }

    public void ExitWriteLock()
    {
        lockSlim.ExitWriteLock();
    }

    public void Add(T item)
    {
        if (!lockSlim.IsWriteLockHeld)
        {
            throw new InvalidOperationException();
        }

        internalList.Add(item);
    }

    public void Remove(T item)
    {
        if (!lockSlim.IsWriteLockHeld)
        {
            throw new InvalidOperationException();
        }

        internalList.Remove(item);
    }

    public T this[int index]
    {
        get
        {
            if (!lockSlim.IsReadLockHeld)
            {
                throw new InvalidOperationException();
            }

            return internalList[index];
        }
        set
        {
            if (!lockSlim.IsWriteLockHeld)
            {
                throw new InvalidOperationException();
            }

            internalList[index] = value;
        }
    }
}
于 2013-06-11T07:29:06.537 に答える