我會盡我所能解釋這一點。如果我摔倒在這,然後我會刪除這個並繼續前進。也許你們可以給我一些想法。在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
是的,我覺得你的問題比這個更深。 –