C#IO读写文件使用错误

C#IO读写文件使用错误,c#,file-io,C#,File Io,我有一个处理读写缓存文件的库。此库由同一台计算机上的Windows服务和控制台应用程序的多个实例使用。当用户登录时,控制台应用程序将运行 我偶尔会收到IO错误,说缓存文件正在被另一个进程使用。我假设不同的应用程序实例和试图同时读写的服务之间发生冲突 是否有一种方法可以在文件使用时锁定该文件,并强制所有其他请求“排队等候”以访问该文件 private void SaveCacheToDisk(WindowsUser user) { string serializedCach

我有一个处理读写缓存文件的库。此库由同一台计算机上的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;
            }
        }
    }

当然,只有在持有互斥锁时,才能使用和允许访问。

请查看
TextWriter.Synchronized
方法

这应该让您可以这样做:

TextWriter.Synchronized(writer).Write(serializedCache);

您可以使用交叉过程。这允许您创建和使用通过名称跨进程标识的WaitHandle。一个线程在轮到它的时候被通知,做了一些工作,然后指示它已经完成,允许另一个线程继续

请注意,这仅在每个进程/线程都引用同一个命名的WaitHandle时才有效


在它们的签名中使用字符串创建命名的系统同步事件。

< P>一个选项,您可以考虑让控制台应用程序通过服务路由他们的文件访问,这样只有一个进程访问文件,并且可以同步访问其中的文件。 实现这一点的一种方法是通过(这里来自weblogs.asp.net)。我们在我工作的公司的一个项目中使用了这项技术,效果很好,我们的具体案例为.net Web服务提供了一种与运行在同一台机器上的Windows服务对话的方法

基于weblogs.asp.net示例的示例

基本上,您需要使用下面的代码创建一个解决方案,添加两个控制台应用程序(一个称为“服务器”,另一个称为“客户端”,并向其中添加一个库。将对库的引用添加到两个控制台应用程序,将下面的代码粘贴到中,并将对System.Runtime.Remoting的引用添加到服务器和控制台

运行服务器应用程序,然后运行客户端应用程序。请注意服务器应用程序有客户端传递给它的消息。您可以将此扩展到任意数量的消息/任务

// 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 !";
        }
    }
}

+1:这看起来正是OP需要的。事实上,
StreamWriter
类上的MSDN明确建议将
StreamWriter
替换为
TextWriter.Synchronized
,如果需要线程安全的输出。Synchronized是一种静态方法。我相信这只适用于进程内同步。问题n实际上是进程间同步,只有互斥体才有操作系统级别的作用域。这不起作用。我将它应用于每个StreamReader和StreamWriter。不过谢谢你的建议。@Aaronaught:哦,是的,你说得对。我没有对OP想要的东西给予足够的关注。@Rob这听起来很有趣。你能解释一下b吗它提供了更多或一些psuedo代码?很抱歉,我以前从未做过类似的事情。@modernzombie-我能建议的最好方法是,您在两个参考页面中的一个页面上按照示例进行操作,我将尝试编写一个更简单的示例,因此请注意以下空间:)@Rob谢谢。当我发布评论时,我没有看到这两个示例。令人恼火的是,weblogs.asp.net中的示例无法直接运行。我将在我编译并运行(基于它)的答案中加入一个代码示例,该答案适用于我=)@robawesome。我真的很感激!我现在正在尝试。