2012-04-22 2 views
2

나는 꽤 복잡한 프로그래밍 문제가 내 손에 있으므로 몇 분 동안 나와 함께 감내해야합니다.동일한 프로그램의 여러 인스턴스간에 동기화하기

나는 WPF (C#)에서 미디어 플레이어를 만들고 난 다음 피클 비트를 실행하기로 결정했습니다.

내 응용 프로그램을 단일 인스턴스로 설정하여 사용자가 서버 파일을 두 번 클릭하면 프로그램이 한 번만 실행되고 모든 파일이 대기하도록합니다.

이 아마 인터넷에 있었다 (나는 마이크로 소프트의 단일 인스턴스 구현을 포함하여, 그것을 몇 가지 방법을 시도하고, 아무것도 작동하는 것 같았다, 내가 내 자신의을 만들기로 결정 때까지, 나는 무언가의 불구하고 그것을 구현 어딘가에 있지만 나타나지 않았다.)

기본적으로 나는 하나 이상의 인스턴스가 열리지 않도록하고, 다른 인스턴스가 파일에 인수를 쓰도록 강제하기 위해 명명 된 뮤텍스를 사용한다. 뮤텍스를 생성 한 인스턴스가 파일을 읽습니다. 말할 필요도없이 이것은 성능면에서 매우 비효율적이지만, 어쨌든, 여기에 Main() 함수를 구현했습니다. 이 Main()도 VS2010에 의해 자동으로 생성 된 것을 싫어하므로 처음부터 작성되었음을 유의하십시오. 나는 또한이 클래스를 사용하고

static void Main(string[] args) 
    { 

      string[] arguments = new string[0]; 
      handler g = new handler(); 
      bool createdNew = false; 
      Mutex lolpaca = new Mutex(true, "lolpacamaximumtrolololololol", out createdNew); 
      if (createdNew) 
      { 

       if (args != null) 
       { 
        var MainWindow = new MainWindow(); 
        var app = new Application(); 
        app.Run(MainWindow); 
        lolpaca.ReleaseMutex(); 
        lolpaca.Dispose(); 
       } 
       else 
       { 
          Array.Resize(ref arguments, 1); 
          arguments[0] = args[0]; 
          string line; 
        //nu mai arunca exceptii nenorocitule 

          while ((line = g.ReadArgs()) != null) 
          { 
           int old_size = arguments.Length; 
           Array.Resize(ref arguments, arguments.Length + 1); 
           arguments[old_size] = line; 
          } 


        var MainWindow = new MainWindow(arguments, arguments.Length); 
        var app = new Application(); 
        app.Run(MainWindow); 
        lolpaca.ReleaseMutex(); 
        lolpaca.Dispose(); 

       } 
       if (File.Exists(path)) 
       { 
        File.Delete(path); 
       } 
      } 

      else 
      { 
       Thread writer = new Thread(new ParameterizedThreadStart(g.WriteArg)); 
       writer.Start(args); 
       writer.Join(); 

       try 
       { 
        g.WriteArg(args); 
       } 
       catch (IOException e) 
       { 
        MediaPlayerFinal_GUI_new.ExceptionCatcher exp = new MediaPlayerFinal_GUI_new.ExceptionCatcher(e.Source); 
        exp.Show(); 
       } 

      } 

    } 

) (메인의 각 더블 클릭 된 파일에 대해 기본적으로

public class handler 
{ 
    static string path = @"D:\playlist.txt"; 
    static FileStream fs = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); 
    string line; 

    string arg; 
    bool readerFlag = false; 
    public string ReadArgs() 
    { 
     try 
     { 
      lock (fs) // Enter synchronization block 
      { 
       if (!readerFlag) 
       {   // Wait until writer finishes 
        try 
        { 
         // Waits for the Monitor.Pulse in WriteArg 
         Monitor.Wait(fs); 
        } 
        catch (SynchronizationLockException) 
        { 

        } 
        catch (ThreadInterruptedException) 
        { 

        } 
       } 


       TextReader tr = new StreamReader(fs); 
       while ((line = tr.ReadLine()) != null) 
       { 
        arg = line; 
       } 
       tr.Close(); 
       tr.Dispose(); 

      } 

      /* fs.Close(); 
      fs.Dispose();*/ 
      readerFlag = false; 
      Monitor.Pulse(fs); 
      return arg; 
     } 
     catch (IOException e) 
     { 
      MediaPlayerFinal_GUI_new.ExceptionCatcher exp = new MediaPlayerFinal_GUI_new.ExceptionCatcher(e.Source); 
      exp.Show(); 
      return null; 
     } 
    } 
    public void WriteArg(object args) 
    { 
     lock (fs) 
     { 
      try 
      { 
       if (readerFlag) 
       { 
        try 
        { 
         Monitor.Wait(fs); // Wait for the Monitor.Pulse in ReadArgs 
        } 
        catch (SynchronizationLockException) 
        { 

        } 
        catch (ThreadInterruptedException) 
        { 

        } 
       } 
       arg = Convert.ToString(args); 
       // FileStream fs = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.Read);     
       TextWriter tw = new StreamWriter(fs); 
       tw.WriteLine(args); 
       tw.Close(); 
       tw.Dispose(); 


      } 
      catch (IOException e) 
      { 
       MediaPlayerFinal_GUI_new.ExceptionCatcher exp = new MediaPlayerFinal_GUI_new.ExceptionCatcher(e.Source); 
       exp.Show(); 
      } 
     } 
     /* fs.Close(); 
     fs.Dispose();*/ 
     readerFlag = true; 
     Monitor.Pulse(fs); 
    } 

}

이제 스레드간에 동기화 할 하나 개의 인스턴스을 시도합니다 함수는 Windows에서 만듭니다. 첫 번째 인스턴스는 뮤텍스를 제어하고 수행하려는 모든 작업을 수행합니다. 다른 인스턴스는 인수를 파일에 작성해야합니다.

문제 : 분명히 스레드 (모두)가 제대로 동기화되지 않으며 때때로 IO 예외가 발생합니다. try-catch 블록이 정확히 아무것도 수행하지 않는 것처럼 보이기 때문에 이러한 예외가 발생하는 정확한 위치를 알지 못합니다. 사실, 이것은 try-catch가 작동하는 것보다 조금 더 깊다고 생각합니다.

그래서 사용자가 많은 파일을 두 번 클릭 할 때 생성되는 모든 스레드를 어떻게 동기화합니까? 이 구현은 최대 3 개의 파일 (최대 9 개까지 테스트 됨)을 사용하여 최대 3 개의 더블 클릭 파일 및 때로는 (때로는 작동 함, 그렇지 않은 경우에는) 3 개 이상의 파일로 작동합니다. 인터넷 응용 프로그램의 여러 인스턴스가 독립적으로 실행되는 경우 지금까지 아무 것도 발견하지 못했습니다. 당신이 나에게 예 :

가 감사를 줄 수 있다면

그것은 좋은 것입니다.

+0

프로세스 간 통신에 파일을 사용하지 마십시오. 이미 실행중인 인스턴스에 filespec을 WM_COPYDATA 메시지로 보내고, 파이프를 사용하고, TCP를 사용하고, 디스크 파일을 제외한 거의 모든 것을 사용하십시오. –

+0

그리고 정확히 어떻게 WPF에서 승리 메시지와 상호 작용합니까? 내가 아는 한 WPF는 그렇게하지 못하게하는 것이 가장 좋습니다. –

+0

신경 쓰지 마세요 : P –

답변

0

동일한 응용 프로그램의 두 인스턴스를 서로 이야기하는 가장 좋은 방법은 IPC를 사용하는 것입니다. 단일 인스턴스에 도움이 사용할 수있는 클래스의 예를 참조 노호 :

/// <summary> 
     /// Enforces single instance for an application. 
     /// </summary> 
     public class SingleInstance : IDisposable 
     { 
      #region Fields 

      /// <summary> 
      /// The synchronization context. 
      /// </summary> 
      private readonly SynchronizationContext synchronizationContext; 

      /// <summary> 
      /// The disposed. 
      /// </summary> 
      private bool disposed; 

      /// <summary> 
      /// The identifier. 
      /// </summary> 
      private Guid identifier = Guid.Empty; 

      /// <summary> 
      /// The mutex. 
      /// </summary> 
      private Mutex mutex; 

      #endregion 

      #region Constructors and Destructors 

      /// <summary> 
      /// Initializes a new instance of the <see cref="SingleInstance"/> class. 
      /// </summary> 
      /// <param name="identifier"> 
      /// An identifier unique to this application. 
      /// </param> 
      /// <param name="args"> 
      /// The command line arguments. 
      /// </param> 
      public SingleInstance(Guid identifier, IEnumerable<string> args) 
      { 
       this.identifier = identifier; 

       bool ownsMutex; 
       this.mutex = new Mutex(true, identifier.ToString(), out ownsMutex); 

       this.synchronizationContext = SynchronizationContext.Current; 

       this.FirstInstance = ownsMutex; 

       if (this.FirstInstance) 
       { 
        this.ListenAsync(); 
       } 
       else 
       { 
        this.NotifyFirstInstance(args); 
       } 
      } 

      /// <summary> 
      /// Initializes a new instance of the <see cref="SingleInstance"/> class. 
      /// </summary> 
      /// <param name="identifier"> 
      /// An identifier unique to this application. 
      /// </param> 
      public SingleInstance(Guid identifier) 
       : this(identifier, null) 
      { 
      } 

      #endregion 

      #region Public Events 

      /// <summary> 
      /// Event raised when arguments are received from successive instances. 
      /// </summary> 
      public event EventHandler<OtherInstanceCreatedEventArgs> OtherInstanceCreated; 

      #endregion 

      #region Public Properties 

      /// <summary> 
      /// Gets a value indicating whether this is the first instance of this application. 
      /// </summary> 
      public bool FirstInstance { get; private set; } 

      #endregion 

      #region Implemented Interfaces 

      #region IDisposable 

      /// <summary> 
      /// The dispose. 
      /// </summary> 
      public void Dispose() 
      { 
       this.Dispose(true); 
       GC.SuppressFinalize(this); 
      } 

      #endregion 

      #endregion 

      #region Methods 

      /// <summary> 
      /// Clean up any resources being used. 
      /// </summary> 
      /// <param name="disposing"> 
      /// True if managed resources should be disposed; otherwise, false. 
      /// </param> 
      protected virtual void Dispose(bool disposing) 
      { 
       if (this.disposed) 
       { 
        return; 
       } 

       if (disposing) 
       { 
        if (this.mutex != null && this.FirstInstance) 
        { 
         this.mutex.WaitOne(); 
         this.mutex.ReleaseMutex(); 
         this.mutex = null; 
        } 
       } 

       this.disposed = true; 
      } 

      /// <summary> 
      /// Fires the OtherInstanceCreated event. 
      /// </summary> 
      /// <param name="arguments"> 
      /// The arguments to pass with the <see cref="OtherInstanceCreatedEventArgs"/> class. 
      /// </param> 
      protected virtual void OnOtherInstanceCreated(OtherInstanceCreatedEventArgs arguments) 
      { 
       EventHandler<OtherInstanceCreatedEventArgs> handler = this.OtherInstanceCreated; 

       if (handler != null) 
       { 
        handler(this, arguments); 
       } 
      } 

      /// <summary> 
      /// Listens for arguments on a named pipe. 
      /// </summary> 
      private void Listen() 
      { 
       try 
       { 
        using (var server = new NamedPipeServerStream(this.identifier.ToString())) 
        { 
         using (var reader = new StreamReader(server)) 
         { 
          server.WaitForConnection(); 
          var arguments = new List<string>(); 

          while (server.IsConnected) 
          { 
           arguments.Add(reader.ReadLine()); 
          } 

          this.synchronizationContext.Post(o => this.OnOtherInstanceCreated(new OtherInstanceCreatedEventArgs(arguments)), null);       
         } 
        } 

        // start listening again. 
        this.Listen(); 
       } 
       catch (IOException) 
       { 
        // Pipe was broken, listen again. 
        this.Listen(); 
       }   
      } 

      /// <summary> 
      /// Listens for arguments being passed from successive instances of the applicaiton. 
      /// </summary> 
      private void ListenAsync() 
      { 
       Task.Factory.StartNew(this.Listen, TaskCreationOptions.LongRunning); 
      } 

      /// <summary> 
      /// Passes the given arguments to the first running instance of the application. 
      /// </summary> 
      /// <param name="arguments"> 
      /// The arguments to pass. 
      /// </param> 
      private void NotifyFirstInstance(IEnumerable<string> arguments) 
      { 
       try 
       { 
        using (var client = new NamedPipeClientStream(this.identifier.ToString())) 
        { 
         using (var writer = new StreamWriter(client)) 
         { 
          client.Connect(200); 

          if (arguments != null) 
          { 
           foreach (string argument in arguments) 
           { 
            writer.WriteLine(argument); 
           } 
          } 
         } 
        } 
       } 
       catch (TimeoutException) 
       { 
        // Couldn't connect to server 
       } 
       catch (IOException) 
       { 
        // Pipe was broken 
       } 
      } 



#endregion 
    } 

/// <summary> 
/// Holds a list of arguments given to an application at startup. 
/// </summary> 
public class OtherInstanceCreatedEventArgs : EventArgs 
{ 
    #region Constructors and Destructors 

    /// <summary> 
    /// Initializes a new instance of the <see cref="OtherInstanceCreatedEventArgs"/> class. 
    /// </summary> 
    /// <param name="args"> 
    /// The command line arguments. 
    /// </param> 
    public OtherInstanceCreatedEventArgs(IEnumerable<string> args) 
    { 
     this.Args = args; 
    } 

    #endregion 

    #region Public Properties 

    /// <summary> 
    /// Gets the startup arguments. 
    /// </summary> 
    public IEnumerable<string> Args { get; private set; } 

    #endregion 
} 

그 다음 주 수업 시간에 당신은 aplication이 실행될 때까지 남아있을 것입니다 클래스의 인스턴스를 만들 수 있습니다. FirstInstance 속성으로 다른 인스턴스가 생성되었는지 확인하고 OtherInstanceCreated 이벤트로 생성 된 다른 인스턴스에 대한 알림을받을 수 있습니다.

+0

예를 들어 주셔서 감사합니다. 이것과 WM을 시도해 보겠습니다. P –

+0

위의 SingleInstance 구현을 사용하여 어딘가에서 ArgumentException을 계속 가져옵니다. –

관련 문제