4

私は車輪を再発明しているように感じます.誰かがすでにコードに頭をぶつけて、この問題を解決する優れた安定したテスト済みのパターンを思いついた可能性が十分にあります.まだ遭遇していません。

私は今私のために働くと思われる次の解決策を思いつきました。
スレッドセーフな方法でアクセスする必要があるオブジェクトを操作するための一貫したインターフェイスを提供することになっています。

@pstはこれを「アトミック」オブジェクト get/set holderと呼んでいますが、これは他の場所で使用されているパターンですか?

インターフェースは次のとおりです。

public interface ISynched<T>
{
    bool Read( ref T value );
    bool Read( ref T value, TimeSpan timeout );

    bool Write( T value );
    bool Write( T value, TimeSpan timeout );

    bool Do( Action<T> roAction );
    bool Do( Action<T> roAction, TimeSpan timeout );
    bool Do( Action<T, Action<T>> rwAction );
    bool Do( Action<T, Action<T>> rwAction, TimeSpan timeout );
}

実装は次のとおりです。

public class Synched<T>: ISynched<T>
{
    static public readonly TimeSpan Infinity = TimeSpan.FromMilliseconds(-1);

    private T _value;

    public static Synched<T> MakeSynched( T value )
    {
        return new Synched<T>() { _value = value };
    }

    private Synched() {}

    public bool Read( ref T value )
    {
        return Read( ref value, Infinity );
    }
    public bool Read( ref T value, TimeSpan timeout )
    {
        var tmp = default(T);
        var success = Do( (v) => tmp = v, timeout );
        if( success ) value = tmp;
        return success;
    }

    public bool Write( T value )
    {
        return Do( (v, set) => set(v) );
    }
    public bool Write( T value, TimeSpan timeout )
    {
        return Do( (v, set) => set(v), timeout );
    }

    public bool Do( Action<T> roAction )
    {
        return Do( roAction, Infinity );
    }
    public bool Do( Action<T> roAction, TimeSpan timeout )
    {
        bool lockWasTaken = false;
        try
        {
            Monitor.TryEnter(this, timeout, ref lockWasTaken);
            if(!lockWasTaken) return false;

            roAction( _value );
            return true;
        }
        finally
        {
            if (lockWasTaken) Monitor.Exit(this);
        }
    }

    public bool Do( Action<T, Action<T>> rwAction )
    {
        return Do( rwAction, Infinity);
    }
    public bool Do( Action<T, Action<T>> rwAction, TimeSpan timeout )
    {
        bool lockWasTaken = false;
        try
        {
            Monitor.TryEnter(this, timeout, ref lockWasTaken);
            if(!lockWasTaken) return false;

            rwAction( _value, value => _value = value );
            return true;
        }
        finally
        {
            if (lockWasTaken) Monitor.Exit(this);
        }
    }
}

Synched オブジェクトの作成をコーディングしやすくするための静的で非ジェネリックなクラスを追加します。

public static class Synched
{
    public static Synched<T> MakeSynched<T>( T value )
    {
        return Synched<T>.MakeSynched( value );
    }
}

編集: より意味のあるように例を変更しました サンプルの使用例は次のようになります (コードは何の意味もありません。単なる例です (悪い例です):

var synchedCol = Synched.MakeSynched( new List<SomeClass>() );

synchedCol.Do( c => {
    c.Add(new SomeClass());
    c.Add(new SomeClass() { Property1 = "something" } );
} );

var i = 1;
SomeClass val;
synchedCol.Do( c => val = c[i] );

var i = 1;
synchedCol.Do( c => {
    if( c[i].Property1 == "something" )
    {
        c.Remove(c[i]);
    }
});

それで、私は正しい軌道に乗っていますか?誰かが似たようなことに遭遇しましたか?類似した既存のパターンはありますか?

4

2 に答える 2

1

車輪の再発明を試みないでください

スレッド セーフなコレクションが必要な場合は、System.Collections.Concurrent名前空間から 1 つを選択します。

たとえば、BlockingColletion<T>を実装するスレッド セーフ コレクションのブロッキングおよび境界機能を提供しますIProducerConsumerCollection<T>。これは、プロデューサー/コンシューマー (リーダー/ライター) パターンを実装するため、実装よりもはるかにパフォーマンスが高くなります。これは、リーダーを同期する必要がなく、相互にブロックしないことを意味します。

于 2012-06-23T07:59:42.933 に答える
0

あなたが何をしているのかにもよりますがFunc<T,T>、データ項目の既存の値を考慮して、新しい値を生成する必要がある を受け入れるメソッドを持つことをお勧めします。呼び出し元は、実行中に基になるデータ項目が書き込まれた場合にデリゲートが複数回呼び出される可能性に備えておく必要があります。たとえば、2 人の呼び出し元Func<int,int>が を計算する(x) => x+1で同じデータ項目 (最初は 5) を更新しようとすると、両方のデリゲートが 5 の値で呼び出され (6 を計算)、1 つの更新が成功し、もう 1 つの更新が再呼び出しされる可能性があります。そのデリゲートに値 6 を指定して (7 を計算)、その結果を格納します。

競合が深刻でない場合、呼び出し元が関数を複数回呼び出す準備ができていれば、そのアプローチを正しく機能させるのは比較的簡単です。. ただし、関数の計算に非常に長い時間がかかり、リソースの競合が多い場合、単純なアプローチはやや遅くなる可能性があります。ただし、競合を検出し、監視オブジェクトを使用して「時限アドバイザリ ロック」を実装するコードを用意することで、そのパフォーマンスをいくらか合理的にすることができます。アイテムの新しい値が 1 つのクライアントのデリゲートによって計算されている場合、別のクライアントがそのアイテムにアクセスできるようにすると、保留中のクライアントまたは新しいクライアントによって行われた作業が無駄になります。保留中のクライアントの機能が実行されている時間の長さによっては、その努力が何の役にも立たない可能性があるため、そのアクションを無効にしても実際の損失ではない可能性があります。一方で、

于 2013-02-25T23:57:42.530 に答える