我会尽我所能解释这一点。如果我摔倒在这,然后我会删除这个并继续前进。也许你们可以给我一些想法。在WCF自定义端点行为中的字典的JQuery并发问题
这是一个网络应用程序。 (C#4.0(服务器)和JQuery 1.6.1客户端)
在客户端,我们有一堆文本框,当触发.focusout事件时触发AJAX调用,将数据发送到WCF服务。
在服务器端,我们创建了一个自定义端点行为,因为我们有一些安全的东西,所以我们可以在这些AJAX调用时获取用户信息(这对我们很好,所以我不寻找设置帮助。)
问题是当我得到创造性的JQuery的,就像如果我想告诉一堆箱子更新一次(哪怕是2个文本框!)
$(".blahblahClass").focusout(); //fires a bunch of AJAX calls
BOOM。我在JsonAuthCallContextInitializer得到System.IndexOutOfRangeException(假定为线程错误)以下的,在这里,它尝试添加一键本词典在我CallContextInitializer的BeforeInvoke()
:
_PrincipalMap[key] = Thread.CurrentPrincipal;
它的重要一提的是我从AfterInvoke()
这个非常相同的字典中删除一个密钥ok ..作为一个字典,这可能不是线程安全的。所以我加了一些lock
s。 (你会在下面的代码中看到)
我去了ReaderWriterLockSlim
,因为我看到lock
有一些并发问题,ReaderWriterLock也有一些问题。因此,我使用默认的LockRecursionPolicy(意思是我简单地将ReaderWriterLockSlim
构造函数留空)添加了锁(它们在下面的代码中)。
当我再次运行应用程序,我会得到一个LockRecursionException(读锁可能无法在此模式下保持的写入锁被收购)
投掷LockRecursionPolicy.SupportsRecursion
到ReaderWriterLockSlim
构造所作的异常走了,可惜,而不是我的网页更新的所有文本框..
关闭我的头顶(我今天会尝试)可能是使字典本身线程安全。这样的事情:What's the best way of implementing a thread-safe Dictionary?
但我不相信它会解决这里的事情。
更新:所以我尝试了其他一些事情。我决定使用一个ConcurrentDictionary,我甚至决定做什么,并在AfterInvoke()中去掉.Remove。没有骰子。它基本上要么抑制的错误,并且只有一个的.html页面上的文本框将更新,或在BeforeInvoke()半身像,如果你有以上几个文本框更多的更新
仅供参考,静态字典是故意
建议?(下面适用行为的代码)
public class JsonAuthEndpointBehavior : IEndpointBehavior
{
private string _key;
public JsonAuthEndpointBehavior(string key)
{
if (key == null) throw new ArgumentNullException("key");
_key = key;
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
var jsonAuthCallContextInitializer = new JsonAuthCallContextInitializer(_key);
foreach (var operation in endpointDispatcher.DispatchRuntime.Operations)
{
operation.CallContextInitializers.Add(jsonAuthCallContextInitializer);
}
}
protected void AddServerErrorHandlers(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
// Do nothing
}
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
// Do nothing
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
// Do nothing
}
public void Validate(ServiceEndpoint endpoint)
{
// Do nothing
}
}
public class JsonAuthCallContextInitializer : ICallContextInitializer
{
private readonly string _key;
private static readonly Dictionary<int, IPrincipal> _PrincipalMap = new Dictionary<int, IPrincipal>();
private readonly ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
public JsonAuthCallContextInitializer(string key)
{
if (key == null) throw new ArgumentNullException("key");
_key = key;
}
public object BeforeInvoke(InstanceContext instanceContext, IClientChannel channel, Message message)
{
if (WebOperationContext.Current == null)
throw new NotSupportedException("JSON Authentication call context initializer requires HTTP");
if (Thread.CurrentPrincipal != null)
{
var key = Thread.CurrentThread.ManagedThreadId;
cacheLock.EnterReadLock();
try
{
//LockRecursionException here
_PrincipalMap[key] = Thread.CurrentPrincipal;
}
finally
{
cacheLock.ExitReadLock();
}
}
Thread.CurrentPrincipal = new ClaimsPrincipal(new[]
{
new ClaimsIdentity((from c in x.Claims select new Claim(blahblah.ToString())).ToArray())
});
return null;
}
public void AfterInvoke(object correlationState)
{
var key = Thread.CurrentThread.ManagedThreadId;
cacheLock.EnterReadLock();
try
{
if (!_PrincipalMap.ContainsKey(key)) return;
Thread.CurrentPrincipal = _PrincipalMap[key];
}
finally
{
cacheLock.ExitReadLock();
}
cacheLock.EnterWriteLock();
try
{
_PrincipalMap.Remove(key);
}
catch (Exception)
{
cacheLock.ExitWriteLock();
}
}
}
非常好的一点。唉,它并没有解决问题。我将它们更改为WriteLock/ExitWriteLock,并得到以下结果:“在此模式下不允许递归写入锁定采集。”将LockRecursionPolicy更改为.SupportsRecursion给了我之前的同样的问题,并非所有的文本框都会更新。 – KevinDeus
是的,我觉得你的问题比这个更深。 –