c# – ReaderWriterLock在ServiceBehavior构造函数中不起作用
我有一个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. 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. 更改为在代码中使用手动重置事件以进行演示,并注释掉了问题代码. >放置断点 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:
我认为这件事发生了: 你的读者锁定获取是在同一个线程内被解雇的(Task.StartNew方法使用TaskScheduler.Current属性)编写器锁正在工作,因此它不会阻塞,因为它具有与任务相同的priveleges,并且得到了空名单.因此,在您的情况下,您必须选择另一个同步原语. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |