Я звоню стороннему API, который использует OAuth для аутентификации, и мне интересно, как сделать этот потокобезопасным:
var token = _tokenService.GetCurrentToken(); // eg token could be "ABCDEF"
var newToken = oauth.RenewAccessToken(token); // eg newToken could be "123456"
_tokenService.UpdateCurrentToken(newToken); // save newToken to the database
Для этого нужно использовать предыдущий токен каждый раз, когда вызывается RenewAccessToken()
. Но есть проблема, если два пользователя инициируют это одновременно (два разных потока будут запускать код в одно и то же время), и мы получим этот код, выполняемый в следующем порядке:
[Thread 1] var token = _tokenService.GetCurrentToken(); // returns "ABCDEF"
[Thread 2] var token = _tokenService.GetCurrentToken(); // returns "ABCDEF"
[Thread 1] var newToken = oauth.RenewAccessToken("ABCDEF"); // returns "123456"
[Thread 2] var newToken = oauth.RenewAccessToken("ABCDEF");
// throws an invalid token exception
Произошло то, что в потоке 2 он на самом деле должен вызывать oauth.RenewAccessToken("123456");
(потому что это последнее значение токена. Но последний токен еще даже не сохранен в базе данных, поэтому поток 2 всегда имеет неправильное значение для текущего токена.
Что я могу сделать, чтобы это исправить?
Редактировать: было предложено использовать блокировку следующим образом:
private object tokenLock = new object();
lock(tokenLock)
{
var token = _tokenService.GetCurrentToken();
var newToken = oauth.RenewAccessToken(token);
_tokenService.UpdateCurrentToken(newToken);
}
Редактировать 2: Блокировка на самом деле не работает, это из моих журналов:
[43 22:38:26:9963] Renewing now using token JHCBTW1ZI96FF
[36 22:38:26:9963] Renewing now using token JHCBTW1ZI96FF
[36 22:38:29:1790] OAuthException exception
Первое число - это идентификатор потока, а второе - метка времени. Оба потока выполняются в одно и то же время с точностью до миллисекунд. Я не знаю, почему блокировка не смогла остановить поток 36, пока не закончится поток 43.
Редактировать 3: И снова, на этот раз после изменения object tokenLock
на переменную класса вместо локальной переменной блокировка не сработала.
[25 10:53:58:3870] Renewing now using token N95984XVORY
[9 10:53:58:3948] Renewing now using token N95984XVORY
[9 10:54:55:7981] OAuthException exception