2014-10-02 111 views
2

除了Mutex以外,還有什麼能夠以容錯方式同步兩個進程?請耐心等待...Mutex替代進程同步/信號與異步支持?

有一個過程A,它有點片狀,它需要在後臺啓動進程B並繼續。如果進程A成功完成它的任務,它需要通知進程B進行處理,並繼續前進(它不會終止並且線程被重用)。如果進程A因異常,終止等而死亡,進程B需要快速檢測並自行處理。進程A不是一個「進程」,而是一個由不同主機執行的庫,因此進程B不能等待進程A的名稱消失。

輸入互斥量。

這裏過程A由一個測試夾具代表,如果成功,它會調用TestFixtureTearDown並繼續前進,或者測試運行器可能會被終止並且TestFixtureTearDown從未被執行。由於實際過程中,TestFixtureTearDown可能是由不同的線程來一個跑TestFixtureSetUp和創建互斥調用,因此ReleaseMutex有時會拋出ApplicationException : Object synchronization method was called from an unsynchronized block of code.

  1. 我可以強制TestFixtureTearDownReleaseMutex,如果是由不同的線程執行或者以其他方式放棄互斥?

  2. 對於這種容錯「反向」等待/監視器場景,我可以使用Mutex嗎?最好不實施過程A發送心跳給過程B和過程B的跟蹤間隔並超時?互斥體感覺像這樣一個優雅的解決方案,除了偶爾在異步的ApplicationException

namespace ClassLibrary1 
{ 
    public class Class1 
    { 
     private Mutex _mutex; 
     private Process _process; 

     [TestFixtureSetUp] 
     public void TestFixtureSetUp() 
     { 
      _mutex = new Mutex(true, "foo"); 
      _process = Process.Start("ConsoleApplication1.exe"); 
     } 

     [Test] 
     public void Test1() { /* Do stuff */ } 

     [Test] 
     public void Test2() { /* Do async stuff */ } 

     [TestFixtureTearDown] 
     public void TestFixtureTearDown() 
     { 
      _mutex.ReleaseMutex(); 
      _process.WaitForExit(); 
     } 
    } 
} 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      var mutex = Mutex.OpenExisting("foo"); 

      // Start doing stuff 

      try { mutex.WaitOne(); } 
      catch (AbandonedMutexException) { } 
      finally { mutex.ReleaseMutex(); } 

      // Finish doing stuff 
     } 
    } 
} 

回答

1

我最終使用了Mutex,ThreadManualResetEvent的混合。對於未來的谷歌搜索,這裏有一個詳細的測試:

using System; 
using System.Diagnostics; 
using System.Threading; 
using System.Threading.Tasks; 
using NUnit.Framework; 

namespace MutexResetEvent.Tests 
{ 
    public class Class1 
    { 
     private Mutex _mutex; 
     private Thread _thread; 
     private Process _process; 
     private ManualResetEvent _event; 

     [SetUp] 
     public void SetUp() 
     { 
      Console.WriteLine("SetUp: #{0}", Thread.CurrentThread.ManagedThreadId); 

      _event = new ManualResetEvent(false); 
      _thread = new Thread(() => 
      { 
       Console.WriteLine("Thread: #{0}", Thread.CurrentThread.ManagedThreadId); 

       _mutex = new Mutex(true, "MutexResetEvent"); 

       _process = new Process 
       { 
        StartInfo = 
        { 
         FileName = "MutexResetEvent.Worker.exe", 
         //UseShellExecute = false, 
         //RedirectStandardOutput = true 
        } 
       }; 
       //_process.OutputDataReceived += (o, a) => Console.WriteLine(a.Data); 
       _process.Start(); 
       //_process.BeginOutputReadLine(); 

       while (!_event.WaitOne(1000)) 
        Console.WriteLine("Thread: ..."); 

       Console.WriteLine("Thread: #{0}", Thread.CurrentThread.ManagedThreadId); 

       _mutex.ReleaseMutex(); 
       _process.WaitForExit(); 
      }); 
     } 

     [Test] 
     public void Test() 
     { 
      Console.WriteLine("Test: #{0}", Thread.CurrentThread.ManagedThreadId); 

      _thread.Start(); 

      for (var i = 0; i < 3; i++) 
      { 
       Console.WriteLine("Test: ..."); 
       Thread.Sleep(1000); 
      } 

      /* 
      if (Guid.NewGuid().GetHashCode() % 3 == 0) 
       Environment.Exit(1); 
      //*/ 
     } 

     [TearDown] 
     public void TearDown() 
     { 
      Console.WriteLine("TearDown: #{0}", Thread.CurrentThread.ManagedThreadId); 

      Task.Run(() => 
      { 
       Console.WriteLine("Task: #{0}", Thread.CurrentThread.ManagedThreadId); 
       _event.Set(); 
       //_thread.Join(); 
      }).Wait(); 

      for (var i = 0; i < 3; i++) 
      { 
       Console.WriteLine("TearDown: ..."); 
       Thread.Sleep(1000); 
      } 
     } 
    } 
} 

using System; 
using System.Threading; 

namespace MutexResetEvent.Worker 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Console.WriteLine("Worker: #{0}", Thread.CurrentThread.ManagedThreadId); 

      var mutex = Mutex.OpenExisting("MutexResetEvent"); 

      try 
      { 
       while (!mutex.WaitOne(1000)) 
        Console.WriteLine("Worker: ..."); 
      } 
      catch (AbandonedMutexException) 
      { 
       Console.WriteLine("Worker: AbandonedMutexException"); 
      } 

      Console.WriteLine("Worker: #{0}", Thread.CurrentThread.ManagedThreadId); 

      mutex.ReleaseMutex(); 
      Console.WriteLine("Worker: WOO HOO"); 

      Console.ReadLine(); 
     } 
    } 
} 
1

信號量沒有線程關聯。您可以在與獲取其他線程不同的線程上釋放信號量。使用計數爲1的信號量。

+0

謝謝,我在想信號燈,但無法找到如何複製'AbandonedMutexException'行爲時,即啓動過程中意外死亡,沒有信號完成任何事情。這一切都回到一方的心跳和另一方的超時。它是否有*拋棄*狀態/事件? – 2014-10-03 09:44:10

+0

我沒有想到這一點。那時我不認爲信號量是答案。接下來的想法是使庫使用任何IPC機制將其運行的PID傳送到「觀察者」過程。然後您可以等待進程終止。 – usr 2014-10-03 09:50:03