C # IO Ошибка чтения и записи файла при использовании - PullRequest
2 голосов
/ 19 февраля 2010

У меня есть библиотека, которая обрабатывает чтение и запись файла кэша. Эта библиотека используется службой Windows и несколькими экземплярами консольного приложения на одном компьютере. Консольное приложение запускается при входе пользователя в систему.

Иногда я получаю сообщения об ошибках ввода-вывода о том, что файл кэша используется другим процессом. Я предполагаю, что происходят конфликты между различными экземплярами приложения и службой, пытающейся одновременно читать и писать.

Есть ли способ заблокировать файл, когда он используется, и заставить все другие запросы «ждать в очереди» для доступа к файлу?

    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 ]

5 голосов
/ 19 февраля 2010

Конечно, вы можете использовать мьютекс и разрешить доступ только при удерживании мьютекса.

1 голос
/ 19 февраля 2010

Один вариант, который вы могли бы рассмотреть, заключается в том, чтобы консольные приложения перенаправляли свой доступ к файлу через службу, таким образом, есть только один процесс, обращающийся к файлу, и вы можете синхронизировать доступ к нему там.

Одним из способов реализации этого является удаленное взаимодействие по каналу IPC (а вот другой пример от weblogs.asp.net). Мы использовали эту технику в проекте для компании, в которой я работаю, и она работает хорошо, в нашем конкретном случае .net WebService предоставляет способ связи со службой 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 голос
/ 19 февраля 2010

Вы можете использовать кросс-процесс EventWaitHandle . Это позволяет вам создавать и использовать WaitHandle, который идентифицирован по процессам по имени. Поток уведомляется, когда наступает его очередь, выполняет некоторую работу, а затем указывает, что он завершен, что позволяет другому потоку продолжить.

Обратите внимание, что это работает только в том случае, если каждый процесс / поток ссылается на одно и то же имя WaitHandle.

Конструкторы EventWaitHandle со строками в своей подписи создают именованные события синхронизации системы.

0 голосов
/ 19 февраля 2010

Проверьте метод TextWriter.Synchronized.

http://msdn.microsoft.com/en-us/library/system.io.textwriter.synchronized.aspx

Это должно позволить вам сделать это:

TextWriter.Synchronized(writer).Write(serializedCache);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...