异步读/写锁
本文关键字:写锁 异步 | 更新日期: 2023-09-27 18:02:52
现在,我正试图使一个通用的IsoStorageManager,它将读/写和序列化/反序列化类异步,基于这个分析。但是,我知道不同的线程会请求相同的文件进行读/写。
我对这个领域的看法:
-
将所有的read ()/Writes()封装到一个锁中。-不太好,因为我不需要等待写不同的文件
-
为写程序添加某种并发队列。如果同一个文件已经在处理中,写程序应该决定是否要取消前一个任务(重写)或从缓存中获取前一个任务并添加自己的更改(合并)。如果阅读器想要访问同一个文件,只需从队列返回数据。-似乎太复杂了
-
强制读取器为所有写入器使用一个线程。然后,我对访问同一文件的多次尝试没有问题。-似乎是一个临时解决方案,这是这里的主要问题。
编辑1:也许我需要一个线程安全的字典?一旦文件要写入,我会将其名称和数据存储在字典中,因此读者只需从写入器本身获取数据。
有什么建议吗?
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>();
});
}
对于writer也是一样,我想确保在这个过程中没有文件被访问。
EDIT3:啊哈,看起来我在这里找到了一个答案:在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");
如果按一次按钮,它或多或少是ok的(不同的文件不会同时写入,因为它应该在一个完美的世界,但现在让它这样):
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:尝试等待临界区。为单个命令(按钮点击)提供类似于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上了。应该有一个很好的公共访问(不需要登录)。会马上把所有的统计数据移到那里
据我所知,您正计划制作一个能够异步运行的Task
。在我看来,最好的是互斥锁——它被设计用来保护共享资源不被多次访问:
互斥锁的另一个优点是in可以是全局的——您可以使用它来保护进程之间对文件的访问。当两个或两个以上的线程需要同时访问一个共享资源时,系统需要一个同步机制来确保一次只有一个线程使用该资源。互斥锁是一种同步原语,它只授予一个线程对共享资源的独占访问权。如果一个线程获取了一个互斥锁,那么第二个想要获取这个互斥锁的线程将被挂起,直到第一个线程释放这个互斥锁。
据我所知,在WP8.0中写入文件,序列化是同步完成的,然后在一个线程上运行-在获取和释放互斥锁时不会有问题。
在这里你可以找到一个好的模式。
编辑
我还是不明白你想要达到什么目的,问题出在哪里。在我看来,你正在将你的反序列化重定向到ThreadPool线程,然后你可以使代码同步(它不在UI线程上运行)并使用互斥锁。可能还有很多其他的解决方案,但也许这个会有帮助: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(); }
}
});
}