繁体   English   中英

C#:这种方法是否是线程安全的

[英]C# : Whether this approach is thread-safe

我有一个服务类,它请求一些外部服务。 那个外部服务需要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)
            };

好的,没关系。 想想sessionId已经过期了。 抛出BillComInvalidSessionException方法

现在,如果sessionId过期,我会在调用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");
    }

它的工作原理和工作正常。 但我不确定,它是否是线程安全的:)

不,这不是线程安全的。

当多个线程并行设置共享的SessionId字段而另一个线程在其他线程正在使用SessionId时设置SessionId时,您就有竞争。

顺便说一句:看来你在调用LoginAsync()时错过了等待。

您的代码不执行任何类型的同步,并允许并行发出一个或多个登录。 这是否是一个问题完全取决于发出会话ID的远程实现。

这是我能想到的3个主要场景:

  1. 远程登录进程同步每个开发人员密钥的会话ID生成,并保证为并发登录返回相同的会话ID。

  2. 远程登录过程将在每次登录时发出新的会话ID,即使在争用情况下也是如此,但所有会话ID都将保持有效。

  3. 远程登录过程将为每次登录发出新的会话ID,但每次后续登录都会使给定开发人员密钥的先前发布的会话ID无效。

在方案1和2中,由于并发登录过程,可能发生的最坏情况是效率低下(例如,不必要的网络IO)。

但是,对于方案3,事情可能会变得很难看,因为您可能会在发布后立即失去会话ID,因此您可能会以许多重试循环结束。 会话无效的并发请求越多,会得到的最差。

如果要确保代码是独立于远程实现的安全,那么您必须同步SessionId读取和写入,这也涉及同步登录过程。 但是,远程实现行为是其服务合同的一部分,因此您的实现也可以合理地考虑该行为。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM