столкнулась с проблемой и к своему стыду никак не могу сама найти выход из нее. Общее описание желаемого — хочется сделать сборку dataprovider'а на основании IsolatedStorage который будет предоставлять возможность асинхронного получения и сохранения данных. В его задачи входит само инициализация — создание структуры каталогов и файлов, и копирование справочных данных внедренных сборку в соответствующие файлы iso. На MSDN есть справка по поводу асинхронных io операций:
Исходя из нее был написан следующий код:
public Store()
{
// десереализуем конфигурационный файл с описанием требуемой файловой структурыvar assembly = Assembly.GetExecutingAssembly();
var setupData = String.Empty;
using (var stream = assembly.GetManifestResourceStream(this.GetResourceName("setup.json")))
using (var reader = new StreamReader(stream))
setupData = reader.ReadToEnd();
this.structure = JsonConvert.DeserializeObject<FileStructure>(setupData.ToString());
this.store = IsolatedStorageFile.GetStore(scope, null, null);
#if DEBUG
var path =
this.store.GetType()
.GetField("m_RootDir", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(store)
.ToString();
System.Diagnostics.Debug.WriteLine(
"Путь {1}", this.store.AssemblyIdentity, path);
#endif// создаем структуру каталоговforeach (var entity in
this.structure.Entities.Where(x => x.Value.Type == FileStructureItemType.Directory))
if (!this.store.DirectoryExists(entity.Value.Path))
this.store.CreateDirectory(entity.Value.Path);
// создаем необходимые файлы, в случае если имеется одноименный embedded ресурс, копируем его содержаниеvar resources = assembly.GetManifestResourceNames();
var asyncJobs = new List<Task>();
foreach (var entity in
this.structure.Entities.Where(x => x.Value.Type == FileStructureItemType.File))
{
if (!this.store.FileExists(entity.Value.Path))
{
if (resources.Contains(this.GetResourceName(entity.Value.Path)))
{
asyncJobs.Add(this.CopyEmbeddedData(entity.Value.Path));
}
else asyncJobs.Add(Task.Factory.StartNew(
() => { using(this.store.CreateFile(entity.Value.Path)); }));
}
}
Task.WaitAll(asyncJobs.ToArray());
#if DEBUG// && CleanOnCreate
((Action<DirectoryInfo>)(dir =>
{
dir.GetDirectories().ToList().ForEach(x => x.Delete(true));
dir.GetFiles().ToList().ForEach(x => x.Delete());
}))(new DirectoryInfo(path));
Console.WriteLine("DONE!");
#endif
}
пытаясь проверить работоспособность конструктора произвожу многократное создание объекта:
Enumerable.Range(1, 10).ToList().ForEach(x => new Store()); Console.ReadLine();
Проблема заключается в том, что условно на 30 запусков, происходит "зависание" (deadlock?) выполнения. При принудительном прерывании выполнения из студи, вижу, что остановка происходит на выполнении создания файла в методе CopyEmbeddedData :
using(var stream = this.store.CreateFile(name))
Помогите пожалуйста разобраться, что я делаю не так
Здравствуйте, drol, Вы писали:
D>Вы не(внимательно) читали документацию. IsolatedStorageFile не является потокобезопасным типом.
Если я правильно понимаю, что в связи с этим задача по обеспечению потокобезопасности меберов инстанса isf — ложится на меня? В таком случае мне потребуется самой реализовывать объекты блокировок и/или синхронизации потоков? Если да, то каким образом это делается при использовании тасков?
Здравствуйте, ravel, Вы писали:
R>Если я правильно понимаю, что в связи с этим задача по обеспечению потокобезопасности меберов инстанса isf — ложится на меня? В таком случае мне потребуется самой реализовывать объекты блокировок и/или синхронизации потоков? Если да, то каким образом это делается при использовании тасков?
Вообще есть ли смысл использовать асинхронные методы? Сделайте обычными и, для удобства, добавьте асинхронные обертки.
Если же неимется задействовать асинхронные, то доступ к IsolatedStorage ограничиваете Mutex'ом вот так:
Здравствуйте, ravel, Вы писали:
R>Помогите пожалуйста разобраться, что я делаю не так
Попробую суммировать.
Как уже сказали, IsolatedStorageFile непотокобезопасен. По этой причине возникают проблемы при обращении к экземпляру этого класса из нескольких параллельно запущенных асинхронных методов.
Отсюда могло бы следовать, что доступ к этому экземпляру нужно синхронизировать. Обычным способом синхронизации является оператор lock(). Но в теле асинхронного метода его нельзя использовать (вернее так: операторы await нельзя использовать в теле оператора lock()). Поэтому Вам предложили использовать именованные мьютексы.
Однако, если тем или иным способом защитить экземпляр IsolatedStorageFile — проблему это, конечно, решит, но сделает бессмысленным использование асинхронных методов и их параллельный вызов: все обращения к IsolatedStorage выстроятся в очередь.
Поэтому правильным решением было бы просто создавать несколько экземпляров IsolatedStorageFile. Внутри асинхронного метода (а не снаружи, как у Вас).
Это тоже не гарантирует больших преимуществ от параллельности (в любом случае тестить надо), но по крайней мере асинхронный код будет иметь смысл.
Здравствуйте, scale_tone, Вы писали:
_>Как уже сказали, IsolatedStorageFile непотокобезопасен. По этой причине возникают проблемы при обращении к экземпляру этого класса из нескольких параллельно запущенных асинхронных методов. _>Отсюда могло бы следовать, что доступ к этому экземпляру нужно синхронизировать. Обычным способом синхронизации является оператор lock(). Но в теле асинхронного метода его нельзя использовать (вернее так: операторы await нельзя использовать в теле оператора lock()). Поэтому Вам предложили использовать именованные мьютексы.
Кроме мутекса можно использовать выделенный шедулер.
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Здравствуйте, ravel, Вы писали:
R>Если я правильно понимаю, что в связи с этим задача по обеспечению потокобезопасности меберов инстанса isf — ложится на меня?
Ложится. Однако, тут есть зависимость от внутреннего устройства "опотокобезопасиваемого" типа. Может оказаться, что минимальный корректный шаг синхронизации это всё время жизни объекта, например.
На эту тему я слегка посмотрел исходный код IsolatedStorageFile и окрестностей. Там, конечно, ад и Израиль, но вот именно CreateFile(), на беглый взгляд, совершенно тривиален. И что в нём может стабильно воспроизводимо клинить — непонятно.
Вобщем, выложите Ваш полный проект. Возможно, что всё ещё смешнее...
Здравствуйте, scale_tone, Вы писали:
_>Однако, если тем или иным способом защитить экземпляр IsolatedStorageFile — проблему это, конечно, решит, но сделает бессмысленным использование асинхронных методов и их параллельный вызов: все обращения к IsolatedStorage выстроятся в очередь.
Я это понимаю.
_>Поэтому правильным решением было бы просто создавать несколько экземпляров IsolatedStorageFile. Внутри асинхронного метода (а не снаружи, как у Вас). _>Это тоже не гарантирует больших преимуществ от параллельности (в любом случае тестить надо), но по крайней мере асинхронный код будет иметь смысл.
Спасибо, попробую, но это нарушает мою первоначальную идею, обернуть работу с хранилищем в синглтон содержащий внутри единственный инстанс iso и предоставляющий различным потокам возможность получать стрим указанных ресурсов и сохранять обратно данные. Впрочем мне думается что я смогу внутри хранилища реализовать очередь записи, производить последовательную запись, при этом выполнять операции чтения асинхронно. Из моих собственных изысканий в области асинхронной работы с io нашелся этот класс ReaderWriterLock — смогу ли я его использовать для синхронизации или же его использование предполагает использование инстансов класса Thread и следовательно "ручной" организации потоков ?
Здравствуйте, scale_tone, Вы писали:
_>Поэтому правильным решением было бы просто создавать несколько экземпляров IsolatedStorageFile. Внутри асинхронного метода (а не снаружи, как у Вас). _>Это тоже не гарантирует больших преимуществ от параллельности (в любом случае тестить надо), но по крайней мере асинхронный код будет иметь смысл.
IsolatedStorage -- это, по сути, файловое хранилище. То есть на диске.
Если файл уже занят -- выпадет ошибка. То есть, в любом случае, без регулировки доступа не обойтись. Устройство то одно... Пока не научились делать 2 версии системного диска с параллельным доступом.
Здравствуйте, ravel, Вы писали:
R> Спасибо, попробую, но это нарушает мою первоначальную идею,
Ничего это не нарушает. Поскольку экземпляр IsolatedStorageFile сам по себе не обеспечивает никакой синхронизации параллельных читателей/писателей — нет никакой разницы, сколько этих экземпляров создавать: один или много.
R> ReaderWriterLock — смогу ли я его использовать для синхронизации или же его использование предполагает использование инстансов класса Thread и следовательно "ручной" организации потоков ?
Раз хочется именно асинхронные методы — тогда скорее нужен AsyncReaderWriterLock.
Так а Вы точно уверены, что хотите сотворить именно блокирующую синхронизацию?
Здравствуйте, scale_tone, Вы писали:
_>Здравствуйте, ravel, Вы писали:
R>> Спасибо, попробую, но это нарушает мою первоначальную идею,
_>Ничего это не нарушает. Поскольку экземпляр IsolatedStorageFile сам по себе не обеспечивает никакой синхронизации параллельных читателей/писателей — нет никакой разницы, сколько этих экземпляров создавать: один или много.
Испытала предложенное вами решение, и сравнила выполнение синхронных операций и асинхронных. Среднее время выполнения синхронных операции на синтетических данных быстрее на 1-2 мс асинхронных.
_>Раз хочется именно асинхронные методы — тогда скорее нужен AsyncReaderWriterLock. _>Так а Вы точно уверены, что хотите сотворить именно блокирующую синхронизацию?