В моем текущем проекте я работаю с Boo / Rhino DSL (кстати, это здорово).
При копании в коде я наткнулся на следующий фрагмент кода:
engine.Cache.WriteLock( () =>
{
engine.Storage.NotifyOnChange(urls, delegate(string invalidatedUrl)
{
engine.Cache.Remove(invalidatedUrl);
if (!standAloneCompilation.Contains(invalidatedUrl))
standAloneCompilation.Add(invalidatedUrl);
});
});
Смысл здесь довольно ясен: engine.Cache
должен быть защищен от состояния гонки, когда с него удален URL. Проблема, которую я вижу здесь, состоит в том, что действительно защищенным является вызов Storage.NotifyOnChange
, а не Cache.Remove
.
И все, что NotifyOnChange
делает, это берет предоставленный делегат и присоединяет его в качестве обработчика событий к «FileWatcher», который он создает. Таким образом, вместо защиты Cache.Remove
блокировка записи здесь защищает создание FileWatcher и оставляет Cache.Remove
незащищенным.
Я испытываю большое уважение и к Бу, и к Носорогу, что заставляет меня задуматься - что-то здесь упущено? или блокировка записи должна быть действительно перемещена внутри делегата?
Вот код NotifyOnChange, если вам интересно:
public virtual void NotifyOnChange(IEnumerable<string> urls, Action<string> action)
{
lock (pathToFileWatchers)
{
string[] commonPaths = GatherCommonPaths(urls);
foreach (string path in commonPaths)
{
FileSystemWatcher watcher;
if(pathToFileWatchers.TryGetValue(path, out watcher)==false)
{
pathToFileWatchers[path] = watcher = new FileSystemWatcher(path, FileNameFormat);
watcher.EnableRaisingEvents = true;
}
watcher.Changed += delegate(object sender, FileSystemEventArgs e)
{
action(e.FullPath);
};
}
}
}