2014-10-02 113 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