现在,我正在尝试根据a generic IsoStorageManager创建this analytic,它将读取/写入并序列化/反序列化异步类。但是,我知道不同的线程会请求相同的文件进行读/写的情况。
我对这个领域的想法:
将所有Reads()/ Writes()包装成一个锁。 - 不太好,因为我不需要等待写不同的文件。
为编写者添加一些并发队列。如果同一文件已经处理,则编写者应决定是否要取消先前的任务(重写)或从缓存中获取先前的任务并添加自己的更改(合并)。如果读者想要访问同一个文件,只需从队列中返回数据即可。 - 似乎过于复杂。
强制读者为所有作家使用一个帖子。然后,我对访问同一文件的乘法尝试没有任何问题。 - 这似乎是一个临时解决方案,这是主要问题。实际上,它与1相同。
EDIT1:也许我需要一个线程安全的字典?一旦要编写文件,我会将其名称和数据存储在字典中,这样读者就可以从编写器本身获取数据。
有什么建议吗?
EDIT2:
我正在使用任务
public static async Task<T> ReadJsonAsyncTask<T>(this JsonTextReader reader)
{
return await TaskEx.Run(() => jsonSerializer.Deserialize<T>(reader));
}
像这样
public static async Task<T> ReadJsonEx<T>(String fileName)
{
if (String.IsNullOrEmpty(fileName))
return default(T);
return await await Task.Factory.StartNew(async () =>
{
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
using (var stream = new IsolatedStorageFileStream(fileName, FileMode.Open, store))
using (var sr = new StreamReader(stream))
using (var jr = new JsonTextReader(sr))
return await jr.ReadJsonAsyncTask<T>();
});
}
对于编者而言,我希望确保在此过程中不会访问任何文件。
EDIT3:啊哈,看起来我在这里找到了答案:Easy way to save game in WP7 Silverlight?
EDIT4:仅适用于同步调用。不是我的情况。 :(
EDIT5:经过一整天的搜索,我找到了AsyncReaderWriterLock。用法很简单:
private static readonly AsyncReaderWriterLock readerLocker = new AsyncReaderWriterLock();
public static async Task<T> ReadJsonEx<T>(String fileName)
{
if (String.IsNullOrEmpty(fileName))
return default(T);
return await await Task.Factory.StartNew(async () =>
{
using (var locker = await readLocker.ReaderLockAsync())
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
using (var stream = new IsolatedStorageFileStream(fileName, FileMode.Open, store))
using (var sr = new StreamReader(stream))
using (var jr = new JsonTextReader(sr))
return await jr.ReadJsonAsyncTask<T>();
});
}
有时它有效 - 有时 - 不是。
EDIT6:好的,这里有一些关于我的AsyncReaderWriterLock测试用例的更多细节。我有像之前提到的读者和使用自己的AsyncReaderWriterLock的编写器。我有一个带有进度条和按钮的页面。按钮命令是:
SimpleLogger.WriteLine("Starting generation...");
var list = new List<Order>();
//for (var i = 0; i < 10; i++)
list.Add(GenerateOrder());
SimpleLogger.WriteLine("Writing 5 times the same file...");
var res1 = await IsoStorageManager.WriteJsonEx(fileName1, list);
var res2 = await IsoStorageManager.WriteJsonEx(fileName1, list);
var res3 = await IsoStorageManager.WriteJsonEx(fileName1, list);
var res4 = await IsoStorageManager.WriteJsonEx(fileName1, list);
var res5 = await IsoStorageManager.WriteJsonEx(fileName1, list);
SimpleLogger.WriteLine("Writing 5 different files");
var res11 = await IsoStorageManager.WriteJsonEx(fileName1, list);
var res12 = await IsoStorageManager.WriteJsonEx(fileName2, list);
var res13 = await IsoStorageManager.WriteJsonEx(fileName3, list);
var res14 = await IsoStorageManager.WriteJsonEx(fileName4, list);
var res15 = await IsoStorageManager.WriteJsonEx(fileName5, list);
SimpleLogger.WriteLine("Reading 5 times the same");
var res21 = await IsoStorageManager.ReadJsonEx<List<Order>>(fileName1);
var res22 = await IsoStorageManager.ReadJsonEx<List<Order>>(fileName1);
var res23 = await IsoStorageManager.ReadJsonEx<List<Order>>(fileName1);
var res24 = await IsoStorageManager.ReadJsonEx<List<Order>>(fileName1);
var res25 = await IsoStorageManager.ReadJsonEx<List<Order>>(fileName1);
SimpleLogger.WriteLine("Reading 5 times different");
var res31 = await IsoStorageManager.ReadJsonEx<List<Order>>(fileName1);
var res32 = await IsoStorageManager.ReadJsonEx<List<Order>>(fileName2);
var res33 = await IsoStorageManager.ReadJsonEx<List<Order>>(fileName3);
var res34 = await IsoStorageManager.ReadJsonEx<List<Order>>(fileName4);
var res35 = await IsoStorageManager.ReadJsonEx<List<Order>>(fileName5);
SimpleLogger.WriteLine("Done");
如果要按一次按钮,它或多或少都可以(不同的文件不是同时写的,因为它应该在一个完美的世界中,但现在就是这样):
09:03:38.262 [00:00:00.000] Starting generation...
09:03:38.300 [00:00:00.025] Writing 5 times the same file...
09:03:43.126 [00:00:04.811] Writing 5 different files
09:03:47.303 [00:00:04.163] Reading 5 times the same
09:03:50.194 [00:00:02.871] Reading 5 times different
09:03:53.341 [00:00:03.130] Done
如果按下按钮几次以模拟高负荷和混合写入/读取的东西,我得到了这个输出:
08:51:52.680 [00:00:00.000] Starting generation...
08:51:52.722 [00:00:00.028] Writing 5 times the same file...
08:51:52.795 [00:00:00.057] Starting generation...
08:51:52.854 [00:00:00.043] Writing 5 times the same file...
08:51:52.892 [00:00:00.023] Starting generation...
08:51:52.922 [00:00:00.016] Writing 5 times the same file...
08:51:52.943 [00:00:00.006] Starting generation...
08:51:52.973 [00:00:00.016] Writing 5 times the same file...
08:52:06.009 [00:00:13.022] Writing 5 different files
08:52:06.966 [00:00:00.942] Writing 5 different files
08:52:07.811 [00:00:00.778] Writing 5 different files
08:52:08.513 [00:00:00.689] Writing 5 different files
08:52:22.115 [00:00:13.567] Reading 5 times the same
08:52:22.887 [00:00:00.755] Reading 5 times the same
08:52:23.773 [00:00:00.754] Reading 5 times the same
行using (var stream = new IsolatedStorageFileStream(fileName, FileMode.Open, store))
System.IO.IOException occurred
_HResult=-2147024864
_message=[IO.IO_SharingViolation_File]
Arguments: Folder//TestFile1.txt
Debugging resource strings are unavailable. Often the key and arguments provide sufficient information to diagnose the problem. See http://go.microsoft.com/fwlink/?linkid=106663&Version=4.0.50829.0&File=mscorlib.dll&Key=IO.IO_SharingViolation_File
HResult=-2147024864
Message=[IO.IO_SharingViolation_File]
Arguments: Folder//TestFile1.txt
Debugging resource strings are unavailable. Often the key and arguments provide sufficient information to diagnose the problem. See http://go.microsoft.com/fwlink/?linkid=106663&Version=4.0.50829.0&File=mscorlib.dll&Key=IO.IO_SharingViolation_File
Source=mscorlib
StackTrace:
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
InnerException:
EDIT7:尝试awaitable critical section。提供类似于单个命令(按钮点击)的AsyncReaderWriterLock结果:
03:12:05.213 [00:00:00.000] Starting generation...
03:12:05.252 [00:00:00.023] Writing 5 times the same file...
03:12:09.894 [00:00:04.626] Writing 5 different files
03:12:13.700 [00:00:03.792] Reading 5 times the same
03:12:16.831 [00:00:03.115] Reading 5 times different
03:12:20.032 [00:00:03.171] Done
但是碰撞测试(4个快速按钮点击)似乎更稳定:它设法完成任务而没有崩溃。
EDIT8:将所有狗屎移到Google Spreadshit。应该有一个很好的公共访问(不需要登录)。将在一段时间内移动所有统计数据。
答案 0 :(得分:2)
据我所知,你打算制作一个Task
,你可以异步运行。 IMO最好的就是Mutex - 它旨在保护共享资源免受多种访问:
当两个或多个线程需要同时访问共享资源时,系统需要一个同步机制来确保一次只有一个线程使用该资源。 Mutex是一个同步原语,它只允许对一个线程的共享资源进行独占访问。如果某个线程获取了一个互斥锁,那么想要获取该互斥锁的第二个线程将被挂起,直到第一个线程释放该互斥锁。
Mutex 的另一个优点是可以是Global - 您可以使用它来保护进程之间对文件的访问。
据我记得在WP8.0中写入文件时,序列化是同步完成的,然后在一个线程上运行 - 获取和释放Mutex没有问题。
Here you can find a good pattern.
修改强>
我仍然没有得到你想要达到的目标以及问题所在。 IMO您正在将反序列化重定向到ThreadPool线程,然后您可以使代码同步(它不在UI线程上运行)并使用Mutex。可能还有很多其他解决方案,但也许这会有所帮助:
public static async Task<T> ReadJsonEx<T>(String fileName)
{
if (String.IsNullOrEmpty(fileName)) return default(T);
string mutexName = "dependantOnApp" + fileName;
return await Task.Run<T>(() =>
{
using (Mutex myMutex = new Mutex(false, mutexName))
{
try
{
myMutex.WaitOne();
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
using (var stream = new IsolatedStorageFileStream(fileName, FileMode.Open, store))
using (var sr = new StreamReader(stream))
using (var jr = new JsonTextReader(sr))
return jsonSerializer.Deserialize<T>(jr);
}
catch { throw new Exception("Exception"); }
finally { myMutex.ReleaseMutex(); }
}
});
}