Cat*_*een 5 c# synchronization semaphore async-await
SemaphoreSlim.WaitAsync不管用。它跳转到调用完成return currentToken.AccessToken之前并抛出 NullException。GetAccesTokenAsync我也尝试使用 AsyncLock、AsyncSemaphore 和我在网上阅读的其他一些方法,但它似乎对我的情况不起作用。
public static class HttpClientHelper
{
#region members
private static SemaphoreSlim semaphore = new SemaphoreSlim(1, 1);
private static Token currentToken;
#endregion
public static string GetAuthorizeToken(ref HttpClient client, string username, string password)
{
GetToken(client, username, password);
return currentToken.AccessToken;
}
private static async void GetToken(HttpClient client, string username, string password)
{
await semaphore.WaitAsync();
try
{
if (currentToken == null)
{
await GetAccesTokenAsync(client, username, password);
}
else if (currentToken.IsExpired)
{
await GetAccessTokenByRefreshToken(client);
}
}
finally
{
semaphore.Release();
}
}
private static async Task<Token> GetAccesTokenAsync(HttpClient client, string username, string password)
{
List<KeyValuePair<string, string>> requestBody = new List<KeyValuePair<string, string>>();
requestBody.Add(new KeyValuePair<string, string>("Username", username));
requestBody.Add(new KeyValuePair<string, string>("Password", password));
requestBody.Add(new KeyValuePair<string, string>("grant_type", "password"));
try
{
using (var urlEncodedContent = new FormUrlEncodedContent(requestBody))
{
var httpResponse = await client.PostAsync(new Uri(client.BaseAddress + "/api/authentication/token"), urlEncodedContent);
currentToken = await httpResponse.Content.ReadAsAsync<Token>(new[] { new JsonMediaTypeFormatter() });
}
return currentToken;
}
catch (Exception e)
{
Logers.Log.Error($"Error while getting the access token {e}");
return null;
}
}
}
Run Code Online (Sandbox Code Playgroud)
您需要做的第一件事是更改private static async void GetToken()方法声明以返回 Task: private static async Task GetToken()。如果它不返回任务,您将无法等待它完成。(正如 GSerg 所提到的,“async void”是“即发即忘”。)
从同步方法调用异步方法的最基本方法是使用Task.Run(...).Wait(),如下所示。
请注意实际上Task.Run(waitForSem).Wait();将异步调用转变为同步调用的调用。
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static void Main()
{
Parallel.Invoke(() => timeWaitForSem(1), () => timeWaitForSem(2));
Console.ReadLine();
}
static void timeWaitForSem(int id)
{
var sw = Stopwatch.StartNew();
Console.WriteLine($"Thread {id} is waiting for semaphore.");
Task.Run(waitForSem).Wait(); // <=== HERE is the important bit.
Console.WriteLine($"Thread {id} finished waiting for semaphore after {sw.Elapsed}.");
}
static async Task waitForSem()
{
await _semaphore.WaitAsync().ConfigureAwait(false);
// Keep hold of the semaphore for a while.
await Task.Delay(2000).ConfigureAwait(false);
_semaphore.Release();
}
static readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
}
}
Run Code Online (Sandbox Code Playgroud)
该程序的输出将类似于:
Thread 1 is waiting for semaphore.
Thread 2 is waiting for semaphore.
Thread 1 finished waiting for semaphore after 00:00:02.0133882.
Thread 2 finished waiting for semaphore after 00:00:04.0316629.
Run Code Online (Sandbox Code Playgroud)
你绝对不能做的是简单地 putwaitForSem().Wait();而不是Task.Run(waitForSem).Wait();,因为这样你可能会陷入死锁(特别是如果它是从带有消息泵的应用程序调用的,例如 WinForms)。
有关更多信息,请参阅从非异步代码调用异步方法
另一种更有效的方法是使用Microsoft.VisualStudio.Threading.dll. 要使用它,您需要Microsoft.VisualStudio.Threading.dll通过 NuGet 引用或添加它。
这样做的优点是,如果不需要,就不会启动新线程。如果您使用JoinableTaskFactory,代码将如下所示:
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Threading;
namespace ConsoleApp1
{
class Program
{
static void Main()
{
Parallel.Invoke(() => timeWaitForSem(1), () => timeWaitForSem(2));
Console.ReadLine();
}
static void timeWaitForSem(int id)
{
var sw = Stopwatch.StartNew();
Console.WriteLine($"Thread {id} is waiting for semaphore.");
_jtf.Run(async () => await waitForSem().ConfigureAwait(false));
Console.WriteLine($"Thread {id} finished waiting for semaphore after {sw.Elapsed}.");
}
static async Task waitForSem()
{
await _semaphore.WaitAsync().ConfigureAwait(false);
// Keep hold of the semaphore for a while.
await Task.Delay(2000).ConfigureAwait(false);
_semaphore.Release();
}
static readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
static readonly JoinableTaskFactory _jtf = new JoinableTaskFactory(new JoinableTaskContext());
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
5814 次 |
| 最近记录: |