工作进程静态变量异常,采用Redis+SemaphoreSlim的方案是否可行?
多进程环境下Token共享方案的疑问解答
场景与代码背景
我有2台负载均衡服务器,每台配备8个工作进程,希望在单台服务器的所有工作进程间共享一个值。最初的实现代码如下:
static string _token; public async Task<string> GetTokenAsync() { if (_token.isExpire() || string.IsNullOrWhiteSpace(_token)) { await _semaphoreSlim.WaitAsync(); if (_token.isExpire() || string.IsNullOrWhiteSpace(_token)) { try { _token = await GetTokenFromAPIServiceAsync(); } finally { _semaphoreSlim.Release(); } } else _semaphoreSlim.Release(); } return _token; }
但我发现多工作进程下静态变量无法正常工作,推测是多个工作进程操作静态字段导致的。于是改用Redis替代静态字段,实现代码如下:
public async Task<string> GetTokenAsync() { var token = await GetFromRedisAsync(); if (string.IsNullOrWhiteSpace(token)) { await _semaphoreSlim.WaitAsync(); if (token.isExpire() || string.IsNullOrWhiteSpace(token)) { try { token = await GetFromServiceAndSetToRedisAsync(); } finally { _semaphoreSlim.Release(); } } else _semaphoreSlim.Release(); } return token; }
疑问解答
1. SemaphoreSlim能否跨工作进程生效?
SemaphoreSlim是进程内同步原语,仅能管控当前进程内部的线程,完全无法跨工作进程生效。每个工作进程都会创建独立的SemaphoreSlim实例,不同进程的实例之间没有任何关联,跨进程的线程不会被其他进程的SemaphoreSlim限制。
2. 使用Redis搭配SemaphoreSlim做线程锁是否为合理的解决方案?
不合理。你的场景是多工作进程(甚至多服务器),SemaphoreSlim的进程内特性决定了它无法提供跨进程的互斥能力,和Redis搭配根本解决不了多进程并发更新的问题。正确的做法是使用Redis分布式锁来替代SemaphoreSlim,利用Redis的原子性操作实现跨进程、跨服务器的互斥,确保同一时间只有一个实例去调用API获取并更新Token。
3. 是否会出现多个工作进程修改Redis中值的情况?
会。当前代码里的SemaphoreSlim只能限制单个进程内的线程,不同工作进程的线程会各自触发锁逻辑,彼此不受限制,最终可能多个进程同时进入GetFromServiceAndSetToRedisAsync()方法,重复调用API获取Token并更新到Redis,造成不必要的API调用和重复更新。
内容的提问来源于stack exchange,提问作者Negin
相关产品推荐
相关产品推荐

