2010-02-19 3 views
2

캐시 파일 읽기 및 쓰기를 처리하는 라이브러리가 있습니다. 이 라이브러리는 Windows 서비스 및 동일한 시스템의 콘솔 응용 프로그램 인스턴스에서 사용됩니다. 사용자가 로그인하면 콘솔 응용 프로그램이 실행됩니다.C# IO 읽기 및 쓰기 file in use 오류

캐시 파일이 다른 프로세스에서 사용 중임을 알리는 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; 
      } 
     } 
    } 

답변

5

물론, 당신은 mutex를 사용하여 뮤텍스를 유지하는 경우에만 액세스를 허용 할 수있다.

0

TextWriter.Synchronized 방법을 확인하십시오.

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

이이 작업을 수행 할 수 있도록해야합니다

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

+1 : 이것은 OP가 필요로하는 것과 정확히 같습니다. 실제로,'StreamWriter' 클래스의 MSDN은 스레드 안전 출력이 필요할 경우'StreamWriter'를'TextWriter.Synchronized'로 대체 할 것을 명시 적으로 제안합니다. – Brian

+0

동기화는 정적 방법입니다. –

+3

이 작업은 * in-process * 동기화에서만 작동합니다. 질문은 실제로 * 프로세스 간 * 동기화에 관한 것이며, 뮤텍스에만 OS 레벨 범위가 있습니다. – Aaronaught

1

당신은 크로스 프로세스 EventWaitHandle를 사용할 수 있습니다. 이를 통해 프로세스간에 이름으로 식별되는 WaitHandle을 만들고 사용할 수 있습니다. 스레드는 자신의 차례 일 때 알림을 받고, 어떤 작업을 수행 한 다음 다른 스레드가 계속 진행할 수 있도록 완료되었음을 나타냅니다.

모든 프로세스/스레드가 동일한 이름의 WaitHandle을 참조하는 경우에만 작동한다는 점에 유의하십시오.

서명에 문자열이있는 EventWaitHandle constructors은 명명 된 시스템 동기화 이벤트를 만듭니다.

1

콘솔 응용 프로그램이 파일 액세스 에서까지 경로를 지정하도록하는 방법 중 하나는 파일에 액세스하는 프로세스가 하나 뿐이므로 거기에 대한 액세스를 동기화 할 수 있습니다.

이 방법을 구현하는 한 가지 방법은 remoting across an IPC channel입니다 (여기에 weblogs.asp.net의 another example). 우리는이 기술을 제가 일하는 회사의 프로젝트에서 사용 했었고 잘 작동합니다. 특정 웹 서비스가 .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 !"; 
     } 
    } 
} 
+0

@Rob이게 재미있을 것 같은데 좀 더 설명해 주시거나 약간의 의사 코드를 제공해 주시겠습니까? 미안하지만 전에 그런 짓은 한 적이 없어. – modernzombie

+0

@modernzombie - 내가 제안 할 수있는 가장 좋은 점은 두 개의 참조 된 페이지 중 하나에 대한 예제를 따르는 것입니다. 더 간단한 샘플을 함께 작성하면이 공간을 볼 수 있습니다 :) – Rob

+0

@Rob 감사합니다. 나는 코멘트를 올렸을 때 그 두 가지 예제를 보지 못했습니다. – modernzombie