Monitor нельзя удерживать через await: продолжение может попасть на другой поток. Кроме того, три отдельные записи свойств позволяют читателю увидеть смешанную конфигурацию.
Потокобезопасное изменение глобальных конфигураций в многопоточном приложении
Monitor нельзя удерживать через await: продолжение может попасть на другой поток. Кроме того, три отдельные записи свойств позволяют читателю увидеть смешанную конфигурацию.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
В каких ситуациях данный код может приводить к ошибкам и какие подходы помогут устранить эти проблемы?
class SettingsManager
{
public static string ServiceUrl { get; private set; }
public static string ServiceUser { get; private set; }
public static string ServicePassword { get; private set; }
private static object syncRoot = new object();
public static async Task RefreshSettings(string configPath)
{
try
{
Monitor.Enter(syncRoot);
var lines = await File.ReadAllLinesAsync(configPath);
ServiceUrl = lines[0];
ServiceUser = lines[1];
ServicePassword = lines[2];
}
finally
{
Monitor.Exit(syncRoot);
}
}
}
Ответ
Monitor нельзя удерживать через await: продолжение может попасть на другой поток. Кроме того, три отдельные записи свойств позволяют читателю увидеть смешанную конфигурацию.
Для сериализации обновлений используйте SemaphoreSlim.WaitAsync с Release в finally. Прочитайте файл, проверьте число строк и значения, затем создайте неизменяемый объект Settings и атомарно опубликуйте одну ссылку через Interlocked.Exchange.
Читатель должен один раз получить снимок через Volatile.Read и брать все поля из него, иначе даже отдельные корректные геттеры могут относиться к разным версиям. Не публикуйте частичные данные при ошибке чтения; сохраняйте предыдущий снимок. Пароль нельзя писать в логи. В исходнике Monitor.Exit также опасен, если Enter не успел успешно захватить блокировку.