通过WCF提供对对象的并发读写访问

时间:2010-10-11 15:25:21

标签: c# .net multithreading wcf

我有一个.NET 4 WCF服务,它维护一个线程安全的,内存中的对象字典缓存(SynchronizedObject)。我想提供安全的并发访问来读取和修改集合中的集合和对象。使用读写器锁可以安全地修改对象和缓存。

我遇到了提供对缓存中对象的读访问权的问题。我的Read方法返回一个SynchronizedObject,但我不知道如何在WCF序列化SynchronizedObject时优雅地确保没有其他线程正在修改对象。

我尝试将Read return子句放在read-lock中,并在自定义XmlObjectSerializer中设置断点。当调用XmlObjectSerializer :: WriteObject(Stream,object)方法时,SynchronizedObject上不会保持读锁定。

我特别关注以下情况:

  1. 线程A调用Read(int)。执行一直持续到return语句之后。到目前为止,finally也已执行,并且已释放SynchronizedObject上的读锁定。线程A的执行被中断。
  2. 线程B调用Modify(int)获取相同的id。写锁定可用并获得。在获得写锁定并释放它之间的某个时间,线程B被中断。
  3. 线程A重新启动并继续序列化。线程B在同一个SynchronizedObject上有一个写锁定,并且位于某个关键部分的中间,但是线程A正在读取SynchronizedObject的状态,因此将一个可能无效的对象返回给Read(int)的调用者。
  4. 我看到两个选项:

    • 维护一个自定义XmlObjectSerializer,它在调用base.WriteObject(Stream,object)方法之前抓取读锁定,并在之后释放它。我不喜欢这个选项,因为如果一个要序列化的对象与某种类型相匹配,那么子类化和覆盖框架序列化函数来执行某个操作。
    • 在保持读锁定的同时在Read方法中创建SynchronizedObject的深层副本,释放锁定并返回深层副本。我不喜欢这个选项,因为我必须实现许多子类的SynchronizedObject,并且需要维护正确的深拷贝,而深拷贝可能很昂贵。

    我还有其他选择吗?我该如何实现线程安全的Read方法?

    我在下面提供了一个虚拟服务,用于更明确的引用:

    
        public class Service : IService
        {
            IDictionary<int, SynchronizedObject> collection = new Dictionary<int, SynchronizedObject>();
            ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();
    
            public SynchronizedObject Read(int id)
            {
                rwLock.EnterReadLock();
                try
                {
                    SynchronizedObject result = collection[id];
                    result.rwLock.EnterReadLock();
                    try
                    {
                        return result;
                    }
                    finally
                    {
                        result.rwLock.ExitReadLock();
                    }
                }
                finally
                {
                    rwLock.ExitReadLock();
                }
            }
    
            public void ModifyObject(int id)
            {
                rwLock.EnterReadLock();
                try
                {
                    SynchronizedObject obj = collection[id];
                    obj.rwLock.EnterWriteLock();
                    try
                    {
                        // modify obj
                    }
                    finally
                    {
                        obj.rwLock.ExitWriteLock();
                    }
                }
                finally
                {
                    rwLock.ExitReadLock();
                }
            }
    
            public void ModifyCollection(int id)
            {
                rwLock.EnterWriteLock();
                try
                {
                    // modify collection
                }
                finally
                {
                    rwLock.ExitWriteLock();
                }
            }
        }
    
        public class SynchronizedObject
        {
            public ReaderWriterLockSlim rwLock { get; private set; }
    
            public SynchronizedObject()
            {
                rwLock = new ReaderWriterLockSlim();
            }
        }
    

1 个答案:

答案 0 :(得分:0)

新答案

根据您的新信息和更清晰的场景,我相信您希望使用类似于函数式编程的不变性功能。不是序列化可以更改的对象,而是创建一个其他线程无法访问的副本,然后对其进行序列化。

以前(没有价值)回答

来自http://msdn.microsoft.com/en-us/library/system.threading.readerwriterlockslim.enterwritelock.aspx

  

如果其他线程已进入锁定状态   在读模式下,一个调用的线程   EnterWriteLock方法阻塞直到   那些线程已经退出读取模式。   有线程等待的时候   进入写模式,附加线程   试图进入阅读模式或   可升级模式块直到全部   线程等待进入写入模式   要么超时要么写入   模式,然后退出。

因此,您需要做的就是在ModifyObject()中调用EnterWriteLock和ExitWriteLock。您尝试确保同时具有读锁定和写锁定实际上是阻止代码工作。