我有一个处理读写缓存文件的库。 Windows服务和同一台计算机上的控制台应用程序的多个实例使用此库。控制台应用程序在用户登录时运行。
我偶尔会遇到IO错误,说缓存文件正由另一个进程使用。我假设在不同的应用程序实例和尝试同时读写的服务之间发生冲突。
有没有办法在文件正在使用时锁定该文件并强制所有其他请求“排队等待”以访问该文件?
private void SaveCacheToDisk(WindowsUser user) {
string serializedCache = SerializeCache(_cache);
//encryt
serializedCache = AES.Encrypt(serializedCache);
string path = user == null ? ApplicationHelper.CacheDiskPath() :
_registry.GetCachePath(user);
string appdata = user == null ? ApplicationHelper.ClientApplicationDataFolder() :
_registry.GetApplicationDataPath(user);
if (Directory.Exists(appdata) == false) {
Directory.CreateDirectory(appdata);
}
if (File.Exists(path) == false) {
using (FileStream stream = File.Create(path)) { }
}
using (FileStream stream = File.Open(path, FileMode.Truncate)) {
using (StreamWriter writer = new StreamWriter(stream)) {
writer.Write(serializedCache);
}
}
}
private string ReadCacheFromDisk(WindowsUser user) {
//cache file path
string path = user == null ? ApplicationHelper.CacheDiskPath() :
_registry.GetCachePath(user);
using (FileStream stream = File.Open(path, FileMode.Open)) {
using (StreamReader reader = new StreamReader(stream)) {
string serializedCache = reader.ReadToEnd();
//decrypt
serializedCache = AES.Decrypt(serializedCache);
return serializedCache;
}
}
}
答案 0 :(得分:5)
当然,您可以使用mutex并仅在持有互斥锁时允许访问。
答案 1 :(得分:1)
您可以使用跨进程EventWaitHandle。这使您可以创建和使用按名称跨进程标识的WaitHandle。一个线程在轮到它时被通知,做一些工作,然后表明它已经完成,允许另一个线程继续。
请注意,这仅适用于每个进程/线程引用相同名称的WaitHandle。
签名中包含字符串的EventWaitHandle constructors创建命名系统同步事件。
答案 2 :(得分:1)
您可以考虑的一个选项是让控制台应用程序通过路由文件访问,这样只有一个进程访问该文件,您可以在那里同步对它的访问。
实现此目的的一种方法是remoting across an IPC channel(此处来自weblogs.asp.net的another example)。我们在我工作的公司的项目中使用了这种技术并且运行良好,我们的具体案例为.net WebService提供了一种与在同一台机器上运行的Windows服务进行通信的方法。
基于weblogs.asp.net示例的示例
基本上您需要对以下代码执行的操作是创建一个解决方案,添加两个控制台应用程序(一个称为“服务器”,另一个称为“客户端”和一个库。添加对库的引用到两个控制台应用程序,粘贴下面的代码并将System.Runtime.Remoting的引用添加到服务器和控制台。
运行Server应用程序,然后运行客户端应用程序。观察服务器应用程序是否有客户端传递给它的消息。您可以将其扩展到任意数量的消息/任务
// Server:
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Ipc;
namespace RemotingSample
{
public class Server
{
public Server()
{
}
public static int Main(string[] args)
{
IpcChannel chan = new IpcChannel("Server");
//register channel
ChannelServices.RegisterChannel(chan, false);
//register remote object
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(RemotingSample.RemoteObject),
"RemotingServer",
WellKnownObjectMode.SingleCall);
Console.WriteLine("Server Activated");
Console.ReadLine();
return 0;
}
}
}
// Client:
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Ipc;
using RemotingSample;
namespace RemotingSample
{
public class Client
{
public Client()
{
}
public static int Main(string[] args)
{
IpcChannel chan = new IpcChannel("Client");
ChannelServices.RegisterChannel(chan);
RemoteObject remObject = (RemoteObject)Activator.GetObject(
typeof(RemotingSample.RemoteObject),
"ipc://Server/RemotingServer");
if (remObject == null)
{
Console.WriteLine("cannot locate server");
}
else
{
remObject.ReplyMessage("You there?");
}
return 0;
}
}
}
// Shared Library:
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
namespace RemotingSample
{
public class RemoteObject : MarshalByRefObject
{
public RemoteObject()
{
Console.WriteLine("Remote object activated");
}
public String ReplyMessage(String msg)
{
Console.WriteLine("Client : " + msg);//print given message on console
return "Server : I'm alive !";
}
}
}
答案 3 :(得分:0)
查看TextWriter.Synchronized
方法。
http://msdn.microsoft.com/en-us/library/system.io.textwriter.synchronized.aspx
这可以让你这样做:
TextWriter.Synchronized(writer).Write(serializedCache);