加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c# – ReaderWriterLock在ServiceBehavior构造函数中不起作用

发布时间:2020-12-15 22:14:26 所属栏目:百科 来源:网络整理
导读:我有一个WCF服务,其中InstanceContextMode是Single,ConcurrencyMode是Multiple.目的是在实例化时创建值的缓存,而不会阻止不依赖于缓存创建的其他服务调用. 这样,只有尝试获取_classificationsCacheLock上的读锁定的方法才需要等到classificationsCache的值被
我有一个WCF服务,其中InstanceContextMode是Single,ConcurrencyMode是Multiple.目的是在实例化时创建值的缓存,而不会阻止不依赖于缓存创建的其他服务调用.

这样,只有尝试获取_classificationsCacheLock上的读锁定的方法才需要等到classificationsCache的值被填充(classificationsCacheLock.IsWriterLockHeld = false).

然而问题是,尽管在任务线程中获取了写锁定,但是调用WCF继续服务以响应对服务方法的调用GetFOIRequestClassificationsList()导致_classificationsCacheLock.IsWriterLockHeld为假,当它应该为真时.

这是WCF实例化的奇怪行为还是我从根本上错过了一个技巧.

我尝试在构造函数的线程上下文中获取写锁(安全选项)并在生成的任务线程的上下文中(这可能会引入WCF之间的竞争,然后调用GetFOIRequestClassificationsList()函数的速度比调用classificationsCacheLock更快.AcquireWriterLock(Timeout.Infinite);)但是两者都导致classificationsCacheLock.IsWriterLockHeld为假,尽管通过使用thread.sleep阻止了任何竞争条件,在每个相应线程的代码块中交错分开.

[ServiceBehavior(Namespace = Namespaces.MyNamespace,ConcurrencyMode = ConcurrencyMode.Multiple,InstanceContextMode = InstanceContextMode.Single)]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class MyService : IMyService
{
    private static NLog.Logger _logger = NLog.LogManager.GetCurrentClassLogger();

    private List<string> _classificationsCache;

    private ReaderWriterLock _classificationsCacheLock;

    public MyService()
    {
        try
        {
            _classificationsCacheLock = new ReaderWriterLock();
            LoadCache();
        }
        catch (Exception ex)
        {
            _logger.Error(ex);
        }

    }

    private void LoadCache()
    {
        // _classificationsCacheLock.AcquireWriterLock(Timeout.Infinite); 

        Task.Factory.StartNew(() =>
        {
            try
            {
                _classificationsCacheLock.AcquireWriterLock(Timeout.Infinite); // can only set writer on or off on same thread,not between threads
                if (_classificationsCache == null)
                {
                    var cases = SomeServices.GetAllFOIRequests();
                    _classificationsCache = cases.SelectMany(c => c.Classifications.Classification.Select(cl => cl.Group)).Distinct().ToList();
                }

            }
            catch (Exception ex)
            {

                _logger.Error(ex);

            }
            finally
            {

                if (_classificationsCacheLock.IsWriterLockHeld)
                    _classificationsCacheLock.ReleaseWriterLock();
            }

        });//.ContinueWith((prevTask) =>
        //{
        //     if (_classificationsCacheLock.IsWriterLockHeld)
        //          _classificationsCacheLock.ReleaseWriterLock();
        //  });

    }

     public GetFOIRequestClassificationsList_Response GetFOIRequestClassificationsList()
    {
        try
        {

            GetFOIRequestClassificationsList_Response response = new GetFOIRequestClassificationsList_Response();

            _classificationsCacheLock.AcquireReaderLock(Timeout.Infinite);
            response.Classifications = _classificationsCache;
            _classificationsCacheLock.ReleaseReaderLock();

            return response;

        }
        catch (Exception ex)
        {
            _logger.Error(ex);

            if (ex is FaultException)
            {
                throw;
            }
            else
                throw new FaultException(ex.Message);
        }
    }
}

编辑1

由于一些建议是围绕线程池内的不确定性以及Task如何处理线程亲和性,我改变了方法以显式生成一个新线程

var newThread = new Thread(new ThreadStart(() =>
        {
            try
            {
                Thread.Sleep(2000);

                Debug.WriteLine(string.Format("LoadCache - _classificationsCacheLock.GetHashCode - {0}",_classificationsCacheLock.GetHashCode()));
                Debug.WriteLine(string.Format("LoadCache - Thread.CurrentThread.ManagedThreadId-  {0} ",Thread.CurrentThread.ManagedThreadId));


                _classificationsCacheLock.AcquireWriterLock(Timeout.Infinite); // can only set writer on or off on same thread,not between threads
                if (_classificationsCache == null)
                {
                    var cases = SomeServices.GetAllFOIRequests();
                    _classificationsCache = cases.SelectMany(c => c.Classifications.Classification.Select(cl => cl.Group)).Distinct().ToList();
                }

            }
            catch (Exception ex)
            {

                _logger.Error(ex);

            }
            finally
            {

                if (_classificationsCacheLock.IsWriterLockHeld)
                    _classificationsCacheLock.ReleaseWriterLock();
            }

        }));
        newThread.IsBackground = true;
        newThread.Name = "MyNewThread" 
        newThread.Start();

结果仍然相同. classificationsCacheLock.AcquireReaderLock不会等待/阻塞,因为它看似应该.

我还添加了一些诊断来检查是否;

>线程实际上是同一个线程,你不能指望一个R / W.
阻止在同一个线程上
> _classificationsCacheLock的实例完全相同

public GetFOIRequestClassificationsList_Response GetFOIRequestClassificationsList()
????{
????????尝试
????????{

GetFOIRequestClassificationsList_Response response = new GetFOIRequestClassificationsList_Response();

        Debug.WriteLine(string.Format("GetFOIRequestClassificationsList - _classificationsCacheLock.GetHashCode - {0}",_classificationsCacheLock.GetHashCode()));
        Debug.WriteLine(string.Format("GetFOIRequestClassificationsList - Thread.CurrentThread.ManagedThreadId - {0} ",Thread.CurrentThread.ManagedThreadId));

        Thread.Sleep(1000);

         _classificationsCacheLock.AcquireReaderLock(Timeout.Infinite);
        //_classificationsCacheMRE.WaitOne();

        response.Classifications = _classificationsCache;

        _classificationsCacheLock.ReleaseReaderLock();

        return response;

    }
    catch (Exception ex)
    {
        _logger.Error(ex);

        if (ex is FaultException)
        {
            throw;
        }
        else
            throw new FaultException(ex.Message);
    }
}

结果是……

GetFOIRequestClassificationsList - _classificationsCacheLock.GetHashCode - 16265870
GetFOIRequestClassificationsList - Thread.CurrentThread.ManagedThreadId - 9 
LoadCache - _classificationsCacheLock.GetHashCode - 16265870
LoadCache - Thread.CurrentThread.ManagedThreadId-  10

..按此顺序,所以现在我们有一个预期的竞争条件,因为在新创建的线程中获得了写锁定.在构造函数的生成线程已被安排实际运行之前,正在进行实际的WCF服务调用.所以我动了

_classificationsCacheLock.AcquireWriterLock(Timeout.Infinite);

到构造函数,因为这保证在访问任何类字段之前执行.

尽管有证据表明构造函数在不同的WCF线程上初始化为执行服务方法的WCF线程,但AcquireWriterLock仍然没有阻塞.

private void LoadCache()
    {

        _classificationsCacheLock.AcquireWriterLock(Timeout.Infinite);

        Debug.WriteLine(string.Format("LoadCache constructor thread - _classificationsCacheLock.GetHashCode - {0}",_classificationsCacheLock.GetHashCode()));
        Debug.WriteLine(string.Format("LoadCache constructor thread - Thread.CurrentThread.ManagedThreadId-  {0} ",Thread.CurrentThread.ManagedThreadId));

        var newThread = new Thread(new ThreadStart(() =>
        {
            try
            {
                Thread.Sleep(5000);

                Debug.WriteLine(string.Format("LoadCache new thread - _classificationsCacheLock.GetHashCode - {0}",_classificationsCacheLock.GetHashCode()));
                Debug.WriteLine(string.Format("LoadCache new thread - Thread.CurrentThread.ManagedThreadId-  {0} ",Thread.CurrentThread.ManagedThreadId));


              //  _classificationsCacheLock.AcquireWriterLock(Timeout.Infinite); // can only set writer on or off on same thread,not between threads
                if (_classificationsCache == null)
                {
                    var cases = SomeServices.GetAllFOIRequests();
                    _classificationsCache = cases.SelectMany(c => c.Classifications.Classification.Select(cl => cl.Group)).Distinct().ToList();
                }

            }
            catch (Exception ex)
            {

                _logger.Error(ex);

            }
            finally
            {

                if (_classificationsCacheLock.IsWriterLockHeld)
                    _classificationsCacheLock.ReleaseWriterLock();
            }

        }));
        newThread.IsBackground = true;
        newThread.Name = "CheckQueues" + DateTime.Now.Ticks.ToString();
        newThread.Start();
}

AcquireWriterLock再次不阻止并允许分配空引用classificationsCache.

结果是……

LoadCache constructor thread - _classificationsCacheLock.GetHashCode - 22863715
LoadCache constructor thread - Thread.CurrentThread.ManagedThreadId-  9 
GetFOIRequestClassificationsList - _classificationsCacheLock.GetHashCode - 22863715
GetFOIRequestClassificationsList - Thread.CurrentThread.ManagedThreadId - 8 
LoadCache new thread - _classificationsCacheLock.GetHashCode - 22863715
LoadCache new thread - Thread.CurrentThread.ManagedThreadId-  10

编辑2

创建了没有源代码管理的解决方案的副本.

如果您想要解决问题,请上传here.

更改为在代码中使用手动重置事件以进行演示,并注释掉了问题代码.

>放置断点
>在Debug中运行Example.Web
>在浏览器中导航到
‘http://localhost:11164/GetFOIRequestClassificationsList.htm‘和
单击按钮.

MRE有效,ReaderWriterLock无法按预期工作.

.net 4.0 – C#

解决方法

在CaseWork()方法中,每次调用方法时都要创建新的ReaderWriterLock.所以获得的锁只是去了Garbage COllector,而新的那个就出现了.因此,实际上没有正确获取锁定.

为什么不在静态构造函数中创建静态锁?

如果我错了,请纠正我,但如果您要更新缓存我不能.如果这是真的,我建议你简单地使用Lazy< T>类.它是线程安全的,并且在设置值之前保留所有读者.它在内部使用TPL,只是使用:

private Lazy<List<string>> _classificationsCache = new Lazy<List<string>>
(() => 
{
    var cases = SomeServices.GetAllFOIRequests();
    return cases.SelectMany(c => c.Classifications.Classification.Select(cl => cl.Group)).Distinct().ToList();
});

你可以得到这样的价值:

response.Classifications = _classificationsCache.Value;

Update from MSDN:

If the current thread already has the writer lock,no reader lock is
acquired
. Instead,the lock count on the writer lock is incremented.
This prevents a thread from blocking on its own writer lock. The
result is exactly the same as calling AcquireWriterLock,and an
additional call to ReleaseWriterLock is required when releasing the
writer lock.

我认为这件事发生了:

你的读者锁定获取是在同一个线程内被解雇的(Task.StartNew方法使用TaskScheduler.Current属性)编写器锁正在工作,因此它不会阻塞,因为它具有与任务相同的priveleges,并且得到了空名单.因此,在您的情况下,您必须选择另一个同步原语.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读