C#ReadWriterLockSlim解锁?

时间:2018-04-13 16:17:32

标签: c# oauth async-await

在以下代码中:

[System.Diagnostics.CodeAnalysis.SuppressMessage("Await.Warning", "CS4014:Await.Warning")]
        private async Task<bool> Refresh()
        {
            log.Info("Refreshing Token.");
            Debug.WriteLine("Refreshing Token.");
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(TOKEN_URL);
            request.Method = "POST";
            request.ContentType = "application/json; charset=UTF-8";
            request.Headers.Add("Authorization", "basic " + authCode);
            request.Accept = "application/json, text/javascript, */*; q=0.01";
            request.ContentLength = payload.Length;

            log.Debug(request.Headers["Authorization"]);
            Debug.WriteLine(request.Headers["Authorization"]);

            using (Stream writeStream = request.GetRequestStream())
            {
                await writeStream.WriteAsync(payload, 0, payload.Length);
            }

            lock (tokenLock)
            {
                Debug.WriteLine($"Write Lock enabled? {tokenLock.IsWriteLockHeld}");
                tokenLock.EnterWriteLock();
                Debug.WriteLine($"Write Lock enabled? {tokenLock.IsWriteLockHeld}");
            }
            try
            {
                string body;
                using (HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync())
                {
                    int numericStatusCode = (int)response.StatusCode;
                    Debug.WriteLine($"Response Code: {numericStatusCode}");
                    if (response.StatusCode != HttpStatusCode.OK)
                    {
                        log.Error($"!!!!! Request failed. Received HTTP {response.StatusCode}");
                        body = string.Empty;
                    }
                    else
                    {
                        string responseValue = string.Empty;
                        using (Stream responseStream = response.GetResponseStream())
                        {
                            if (responseStream != null)
                            {
                                using (StreamReader reader = new StreamReader(responseStream))
                                {
                                    responseValue = await reader.ReadToEndAsync();
                                }
                            }
                        }
                        body = responseValue;

                        Debug.WriteLine($"Response Body = {body}");
                        log.Trace($"Response Body = {body}");
                    }
                }
                Debug.WriteLine($"Write Lock enabled? {tokenLock.IsWriteLockHeld}");
                if (!string.IsNullOrEmpty(body))
                {
                    _token = JsonConvert.DeserializeObject<AuthTokenInfo>(body, serializerSettings);

                    refreshUri = _token.RefreshTokenServerUri;
                    payload = Encoding.GetEncoding("utf-8").GetBytes(
                        JsonConvert.SerializeObject(new { grant_type = "refresh_token", _token.RefreshToken })
                    );
                    Debug.WriteLine($"Write Lock enabled? {tokenLock.IsWriteLockHeld}");
                    Debug.WriteLine($"Token Refreshed, Expires In = {_token.ExpiresIn}");
                    Debug.WriteLine($"Access Token = {_token.AccessToken}");

                    Debug.WriteLine($"New Token Refresh URI: {refreshUri}");
                    Debug.WriteLine($"New Refresh Token: {_token.RefreshToken}");
                    if (_token != null)
                    {
                        int refreshTime = 60 * 1000; // (Token.ExpiresIn.Value - (15 * 60)) * 1000;
                        log.Info($"Refreshing token in {refreshTime} milliseconds.");
                        Debug.WriteLine($"Refreshing token in {refreshTime} milliseconds.");

                        Task.Delay(refreshTime).ContinueWith(async (action) =>
                        {
                            log.Info("Refreshing token NOW.");
                            Debug.WriteLine("Refreshing token NOW.");
                            await Refresh();
                        });

                        Debug.WriteLine("Refresh scheduled.");
                    }
                }
            }
            finally
            {
                lock(tokenLock)
                {
                    Debug.WriteLine($"Write Lock enabled? {tokenLock.IsWriteLockHeld}");
                    tokenLock.ExitWriteLock();
                    Debug.WriteLine($"Write Lock enabled? {tokenLock.IsWriteLockHeld}");
                }
            }
            return true;
        }

当我执行此代码时,我的调试输出显示:

Refreshing Token.
Write Lock enabled? False
Write Lock enabled? True
Response Code: 200
Write Lock enabled? False
Write Lock enabled? False
Token Refreshed, Expires In = 3600
Refreshing token in 60000 milliseconds.
Refresh scheduled.
Write Lock enabled? False
Exception thrown: 'System.Threading.SynchronizationLockException' in System.Core.dll
Exception thrown: 'System.AggregateException' in mscorlib.dll
Exception thrown: 'System.TypeInitializationException' in InContactApi.dll
Exception thrown: 'System.TypeInitializationException' in mscorlib.dll
Exception thrown: 'System.AggregateException' in mscorlib.dll
An unhandled exception of type 'System.AggregateException' occurred in mscorlib.dll
One or more errors occurred.


Unhandled Exception: System.AggregateException: One or more errors occurred. ---> System.TypeInitializationException: The type initializer for 'InContact.Auth' threw an exception. ---> System.AggregateException: One or more errors occurred. ---> System.Threading.SynchronizationLockException: The write lock is being released without being held.
   at System.Threading.ReaderWriterLockSlim.ExitWriteLock()
   at InContact.AuthToken.<Refresh>d__12.MoveNext() in C:\Users\chill\source\repos\interactive_intelligence\InContactApi\InContactApi\Auth.cs:line 206
   --- End of inner exception stack trace ---
   at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
   at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at System.Threading.Tasks.Task.Wait()
   at InContact.AuthToken..ctor() in C:\Users\chill\source\repos\interactive_intelligence\InContactApi\InContactApi\Auth.cs:line 106
   at InContact.Auth..cctor() in C:\Users\chill\source\repos\interactive_intelligence\InContactApi\InContactApi\Auth.cs:line 236
   --- End of inner exception stack trace ---
   at InContact.Auth.get_BaseURL()
   at InContact.InContactApi.MakeRequestURL(String subURL, Dictionary`2 query, String callerName) in C:\Users\chill\source\repos\interactive_intelligence\InContactApi\InContactApi\InContactApi.cs:line 127
   at InContact.InContactApi.<GetFolderListing>d__26.MoveNext() in C:\Users\chill\source\repos\interactive_intelligence\InContactApi\InContactApi\InContactApi.cs:line 607
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at CallLogger.ICRecordings.<DirTraverse>d__8.MoveNext() in C:\Users\chill\source\repos\interactive_intelligence\CallLogger\CallLogger\ICRecordings.cs:line 73
   --- End of inner exception stack trace ---
   at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
   at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
   at System.Threading.Tasks.Task`1.get_Result()
   at CallLogger.Program.Main() in C:\Users\chill\source\repos\interactive_intelligence\CallLogger\CallLogger\Program.cs:line 32
The program '[34036] PhoneLogger.exe' has exited with code 0 (0x0).

我不明白我的写锁是如何在代码中途解锁的,只有一个写解锁线我在最后一个块的末尾。

任何人都可以为我阐明这一点,并/或建议更好的方法吗?

我正在处理一个OAuth系统,该系统具有必须每小时刷新一次的访问令牌(当我最终完成它时)。我已实现此Refresh方法来完成目标,并使用Task.Delay().ContinueWith()调用来安排刷新自动运行。我正在使用ReadWriterLockSlim,以便我可以在刷新发生时锁定读取继续。否则我希望它们正常工作。我需要锁定它们,因为一旦我在刷新时从服务器请求新令牌,我就不能再使用旧的身份验证令牌了。

1 个答案:

答案 0 :(得分:0)

Aleks Andreev,谢谢你。

所以解决方案是不使用ReadWriterLockSlim,而是安装Nito.AsyncEx NuGet模块,然后使用它的AsyncReadWriterLock。因为ReadWriterLockSlim不能与async / await一起使用。