简体   繁体   English

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

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

I have a service class, which does request to some external service. 我有一个服务类,它请求一些外部服务。 That external service requires sessionId, time of live of this sessionId is unknown (can be some seconds, can be minutes, can be hours, can be days. Can be 1 request, can be 1000 requests. Nobody knows it. We can't link to it at all). 那个外部服务需要sessionId,这个sessionId的生存时间是未知的(可以是几秒钟,可以是几分钟,可以是几小时,可以是几天。可以是1个请求,可以是1000个请求。没人知道。我们不能完全链接到它。

I created a method, which get sessionId: 我创建了一个获取sessionId的方法:

    private async Task<string> LoginAsync()
    {
        using (HttpClient clientLogin = new HttpClient())
        {
            //....
        }
        return sessionId;
    }

I declared the following variable inside my class 我在我的类中声明了以下变量

public class BillComService : IBillComService
{
    private static Lazy<string> SessionId;

and init it in constructor: 并在构造函数中初始化它:

    public BillComService()
    {
        if (SessionId == null)
            SessionId = new Lazy<string>(() => LoginAsync().Result);
        //....
    }

then I use it inside my methods: 然后我在我的方法中使用它:

    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, it's fine. 好的,没关系。 Think about sessionId is expired. 想想sessionId已经过期了。 Method with throw BillComInvalidSessionException 抛出BillComInvalidSessionException方法

Right now I wrote the following method to repeat my request after calling LoginAsync method if sessionId is expired. 现在,如果sessionId过期,我会在调用LoginAsync方法之后编写以下方法来重复我的请求。 Common method: 常用方法:

    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);
        }
    }

and for Read : Read

    private async Task<T> ReadWithRetry<T>(string id, string endpoint)
    {
        return await Retry(async () => await Read<T>(id, endpoint));
    }

so, my public method is: 所以,我的公共方法是:

    public async Task<Customer> GetCustomerAsync(string id)
    {
        return await ReadWithRetry<Customer>(id, "Crud/Read/Customer.json");
    }

it works and work fine. 它的工作原理和工作正常。 But I'm not sure, whether it's thread-safe :) 但我不确定,它是否是线程安全的:)

No, it's not thread safe. 不,这不是线程安全的。

You have a race when multiple threads set the shared SessionId field in parallel and another one when one thread is setting SessionId while other threads are using it. 当多个线程并行设置共享的SessionId字段而另一个线程在其他线程正在使用SessionId时设置SessionId时,您就有竞争。

BTW: it seems you are missing an wait when calling LoginAsync(). 顺便说一句:看来你在调用LoginAsync()时错过了等待。

Your code doesn't perform any kind of synchronization and allows for one or more logins issued in parallel. 您的代码不执行任何类型的同步,并允许并行发出一个或多个登录。 Whether this is a problem or not is entirely dependent on the remote implementation issuing the session IDs. 这是否是一个问题完全取决于发出会话ID的远程实现。

Here's the 3 main scenarios I can think of: 这是我能想到的3个主要场景:

  1. The remote login process synchronizes the session ID generation per developer's key and is guaranteed to return the same session ID for concurrent logins. 远程登录进程同步每个开发人员密钥的会话ID生成,并保证为并发登录返回相同的会话ID。

  2. The remote login process will issue a new session ID per login, even in contention scenarios, but all session ID will remain valid. 远程登录过程将在每次登录时发出新的会话ID,即使在争用情况下也是如此,但所有会话ID都将保持有效。

  3. The remote login process will issue a new session ID per login, but each subsequent logins are invalidating previously issued session IDs for a given developer's key. 远程登录过程将为每次登录发出新的会话ID,但每次后续登录都会使给定开发人员密钥的先前发布的会话ID无效。

With scenarios 1 & 2, the worst that can happen are inefficiencies (eg unnecessary network IO) due to concurrent login processes. 在方案1和2中,由于并发登录过程,可能发生的最坏情况是效率低下(例如,不必要的网络IO)。

However, with scenario 3 then things could get ugly as you may end with many retry loops since session IDs could get invalidated right after being issued. 但是,对于方案3,事情可能会变得很难看,因为您可能会在发布后立即失去会话ID,因此您可能会以许多重试循环结束。 The more concurrent requests there are with an invalid session and the worst it would get. 会话无效的并发请求越多,会得到的最差。

If you want to make sure that your code is safe independently of the remote implementation then you would have to synchronize the SessionId read & writes, which involves synchronizing the login process as well. 如果要确保代码是独立于远程实现的安全,那么您必须同步SessionId读取和写入,这也涉及同步登录过程。 Still, the remote implementation behavior is part of their service contract so it can also be reasonable for your implementation to take that behavior into consideration. 但是,远程实现行为是其服务合同的一部分,因此您的实现也可以合理地考虑该行为。

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

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