C#IO读取和写入文件正在使用错误

时间:2010-02-19 14:43:11

标签: c# file-io

我有一个处理读写缓存文件的库。 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;
            }
        }
    }

4 个答案:

答案 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);