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);
        }
    }
}

还有一个额外的静态非泛型类,可以更轻松地编写同步对象创建代码:

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命名空间中选择一个。

例如BlockingColletion<T>,为实现IProducerConsumerCollection<T>. 这将比您的实现性能更高,因为它实现了生产者/消费者(读取器/写入器)模式。这意味着阅读器不必同步,也不会相互阻塞。

于 2012-06-23T07:59:42.933 回答
0

根据您在做什么,我建议一个好的模式是有一个方法接受一个Func<T,T>应该 - 给定数据项的现有值,产生一个新值;如果底层数据项在执行时被写入,调用者必须为委托可能被多次调用的可能性做好准备。例如,两个调用者尝试使用计算的 a 更新相同的数据项(最初为 5),有Func<int,int>可能(x) => x+1两个委托都将被调用,值为 5(计算 6),一个更新会成功,另一个会重新调用它的委托值为 6(计算 7),然后存储该结果。

当争用不严重时,如果调用者准备多次调用该函数,则该方法相对容易正确工作. 但是,如果函数的计算时间很长,并且对资源有很多争用,那么直接的方法可能会有点慢。然而,通过检测争用并开始使用监控对象来实现“定时咨询锁”的代码,它的性能可以变得有些合理:如果一个项目的新值正在由一个客户端的委托计算,允许另一个客户端访问该项目将导致挂起的客户端或新客户端完成的工作被浪费。取决于挂起的客户端的函数运行了多长时间,它的努力可能永远不会产生任何结果,因此使其操作无效可能不会造成真正的损失。另一方面,

于 2013-02-25T23:57:42.530 回答