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,并且得到了空名单.因此,在您的情况下,您必须选择另一个同步原语. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
