我有一个服务类,这并请求一些外部的服务。外部服务需要的sessionId,这的sessionId活的时间是未知的(可以是几秒钟,可以是几分钟,可以是数小时,可天。可以是1个请求,可以1000项请求,没有人知道这一点。我们不能链接到它在所有)。
我创建了一个方法,该方法得到的sessionId:
private async Task<string> LoginAsync()
{
using (HttpClient clientLogin = new HttpClient())
{
//....
}
return sessionId;
}
我宣布我的类中的以下变量
public class BillComService : IBillComService
{
private static Lazy<string> SessionId;
并且初始化它的构造函数:
public BillComService()
{
if (SessionId == null)
SessionId = new Lazy<string>(() => LoginAsync().Result);
//....
}
然后我用它在我里面的方法:
private async Task<T> Read<T>(string id, string endpoint)
{
var nvc = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("devKey", devKey),
new KeyValuePair<string, string>("sessionId", SessionId.Value)
};
OK,它的罚款。想想的sessionId已过期。与投BillComInvalidSessionException
方法
现在我写了下面的方法,如果会话id过期调用LoginAsync
方法后重复我的要求。常用的方法:
private async Task<T> Retry<T>(Func<Task<T>> func)
{
try
{
return await func();
}
catch (BillComInvalidSessionException)
{
SessionId = new Lazy<string>(() => LoginAsync().Result);
return await Retry(func);
}
}
和Read
:
private async Task<T> ReadWithRetry<T>(string id, string endpoint)
{
return await Retry(async () => await Read<T>(id, endpoint));
}
所以,我的公开方法是:
public async Task<Customer> GetCustomerAsync(string id)
{
return await ReadWithRetry<Customer>(id, "Crud/Read/Customer.json");
}
它的工作原理和做工精细。但我不知道,无论是线程安全的:)
您的代码不执行任何种类的同步,并允许并行发布的一个或多个登录。这是否是一个问题或不完全依赖于远程执行发出会话ID。
这是我能想到的3种主要方案:
与场景1和2,可能发生最坏的是低效率(例如不必要的网络IO)由于并发登录进程。
然而,方案3,那么事情可能因为你可能最终与很多重试循环,因为会话ID可以被正确发出后得到无效变得丑陋。越多的并发请求有一个无效的会话,它会得到最糟糕的。
如果你想确保你的代码是独立于远程执行的安全,那么你就必须同步SessionId
读取和写入,其中包括同步登录过程也是如此。尽管如此,远程实现的行为是他们的服务合同的一部分,所以它也可以是合理的为你实现把这种行为纳入考虑。
不,这不是线程安全的。
你有一个比赛,当多个线程并行设置共享SessionId
场和另一个当一个线程被设置SessionId
而其他线程正在使用它。
BTW:看来你是缺少一个等待调用LoginAsync时()。