2015-12-30 70 views
0

我有一個引發多個事件的服務,其中一些可以同時引發。我需要處理這些事件並根據事件參數運行可能長時間運行的方法。
我所做的是創建一個BlockingCollection<T>,它將存儲事件一個Task,它將一直保持一個事件,直到它將被髮信號停止使用CancellationTokenSource
我擔心的是我沒有足夠好地處理同步。
這是處理一切類(它用作WPF ViewModel):多事件和多線程

public class EventsTest 
{ 
    //private fields 
    private BlockingCollection<IoEventArgs> _queue; 
    private CancellationTokenSource _tokenSource; 
    private IoService _ioService; 
    private Task _workerTask; 
    private static EventWaitHandle _eventWaiter; 

    public EventsTest() 
    { 
     _queue = new BlockingCollection<IoEventArgs>(); 
     _tokenSource = new CancellationTokenSource(); 
     _eventWaiter = new EventWaitHandle(false, EventResetMode.AutoReset); 

     //this is the object that raises multiple events 
     _ioService = new IoService(); 
     _ioService.IoEvent += _ioService_IoEvent; 

     //Start Listening 
     var t = Task.Factory.StartNew(StartListening, _tokenSource, TaskCreationOptions.LongRunning); 
    } 

    //IO events listener 
    private void _ioService_IoEvent(string desc, int portNum) 
    { 
     //add events to a blocking collection 
     _queue.Add(new IoEventArgs() { Description = desc, PortNum = portNum }); 
    } 

    private void StartListening(object dummy) 
    { 
     //process the events one at a time 
     while (!_tokenSource.IsCancellationRequested) 
     { 
      var eve = _queue.Take(); 
      switch (eve.PortNum) 
      { 
       case 0: 
        LongRunningMethod(eve.Description); 
        break; 
       case 1: 
        //invoke a long running method 
        break; 
       default: 
        break; 
      } 
     } 
    } 

    //sample long running method 
    private void LongRunningMethod(string data) 
    { 
     _eventWaiter.WaitOne(10000); 
    } 
} 

我怎樣才能讓這個過程更健壯的線程安全的條款?
在每個方法實現周圍添加一個lock是否會提高過程的安全性?

回答

1

您的.Take()不會被取消,因此您可能會永遠在那裏等待。

你可以通過在令牌:

var eve = _queue.Take(_tokenSource); 

但隨後你就必須處理異常。

更好的方法是TryTake(out eve,1000,_tokenSource)並用返回的布爾值操縱。

或者忘記的CancellationToken,只是使用AddingComplete()

+0

不允許傳遞'_tokenSource':'無法從'System.Threading.CancellationTokenSource'轉換爲'System.Threading.CancellationToken''。我需要爲此創建一個單獨的'CancellationToken'嗎? – Yoav

+0

是的,源具有.Token屬性。 –

0

這聽起來像是一種情況,微軟的反應框架是一個更好的選擇。

您的代碼應該是這樣的:

public class EventsTest 
{ 
    private IDisposable _subscription; 

    public EventsTest() 
    { 
     IoService ioService = new IoService(); 

     _subscription = 
      Observable 
       .FromEvent<IoEvent, IoEventArgs>(
        a => ioService.IoEvent += a, a => ioService.IoEvent -= a) 
       .Subscribe(eve => 
       { 
        switch (eve.PortNum) 
        { 
         case 0: 
          LongRunningMethod(eve.Description); 
          break; 
         case 1: 
          //invoke a long running method 
          break; 
         default: 
          break; 
        } 
       }); 
    } 

    private void LongRunningMethod(string data) 
    { 
    } 
} 

這應自動確保多個事件進行排隊,永遠不會重疊。如果出現問題,請在.Subscribe(...)之前撥打.Synchronize(),然後完美運行。

當你想取消事件只需致電_subscription.Dispose(),它會全部爲你清理。

NuGet「Rx-Main」獲取您需要的位。