多个事件和多线程

时间:2015-12-30 09:37:47

标签: c# wpf multithreading events .net-4.6

我有一个提升多个事件的服务,其中一些可以同时引发。我需要处理这些事件并根据事件参数运行一个可能长时间运行的方法 我所做的是创建一个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可以提高流程的安全性?

2 个答案:

答案 0 :(得分:1)

您的.Take()未被取消,因此您可能会永远等待。

你可以传递令牌:

var eve = _queue.Take(_tokenSource);

但是你必须处理异常。

更好的方法是TryTake(out eve,1000,_tokenSource)并使用返回的布尔值进行操纵。

或者忘掉CancellationToken并使用AddingComplete()

答案 1 :(得分:0)

这听起来像是微软的Reactive Framework更适合的情况。

您的代码如下所示:

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)
    {
    }
}

这应该会自动确保多个事件排队并且永远不会重叠。如果出现问题,只需在.Synchronize()之前放弃.Subscribe(...)来电,然后它就能完美运行。

当您想要取消活动时,只需致电_subscription.Dispose(),它就会全部为您清理。

NuGet&#34; Rx-Main&#34;得到你需要的东西。