繁体   English   中英

带锁事件处理的最有效设计模式

[英]Most efficient design pattern for event handling with locking

我有一个应用程序,该应用程序从API异步接收事件,并且可以在此API上同步调用方法。

为了线程安全,我需要锁定应用程序中的每个同步函数和每个事件处理程序。

但是,同步调用API方法可能会导致API在其他线程上引发事件,并在返回之前等待事件被处理。

因此,这可能会导致死锁,因为API将等待事件的继续处理,但是在我的类中,同步对象将被两个不同的线程击中,并且程序将挂起。

我当前的想法是尝试锁定而不是锁定事件处理程序,并且在不可能的情况下(例如,如果事件是由另一个线程上的同步调用导致的)将事件缓冲在队列/消息泵中。

在释放对同步函数调用的锁定之前,然后我将调用ProcessPendingEvents()函数,以便可以处理事件而不会出现死锁。

对于这种情况,您有没有建议的设计模式? 我对任何事情都开放。

这是一个简单的示例来说明我当前的暂定实现。 我的真正目的是使类尽可能地以单线程方式运行:

class APIAdapter {
    readonly object AdapterLock = new object();
    private readonly ConcurrentQueue<Tuple<object, EventArgs>> PendingEvents = new ConcurrentQueue<Tuple<object, EventArgs>>();
    ExternalAPI API = new ExternalAPI();

    APIAdapter() {
        ExternalAPI.Data += ExternalAPI_Data;
    }

    public void RequestData() {
        lock (this.AdapterLock) {
            this.ExternalAPI.SynchronousDataRequest(); //Will cause the API to raise the Data event would therefore deadlock if I had a simple lock() in ExternalAPI_Data.
            this.ProcessPendingEvents();
        }
    }

    private void ExternalAPI_Data(object sender, EventArgs e) {
        if (!Monitor.TryEnter(this.AdapterLock)) {
            this.PendingEvents.Enqueue(Tuple.Create(sender, e));
            return;
        }
        Console.Write("Received event.");
        Monitor.Exit(this.AdapterLock);
    }

    private void ProcessPendingEvents() {
        Tuple<object, EventArgs> ev;
        while (this.PendingEvents.TryDequeue(out ev)) {
            ExternalAPI_Data(ev.Item1, ev.Item2);
        }
    }
}

我最初的解决方案并不令人满意: after ProcessPendingEvents()完成之后,但在释放锁之前,可以缓冲其他事件,直到下一次调用ProcessPendingEvents()时才引发其他事件。

如果API在不同的线程上发送回两个事件,则事件也可以在任何时候进行缓冲,而我非常缺乏一种在释放锁后立即消耗这些事件的方法。

我最终使用BlockingCollection实现了更为简洁的生产者/消费者模式,以控制何时处理API事件。 以下是有兴趣者的相应代码:

class APIAdapter {
    readonly object AdapterLock = new object();
    private readonly BlockingCollection<Tuple<object, EventArgs>> PendingEvents = new BlockingCollection<Tuple<object, EventArgs>>();
    ExternalAPI API = new ExternalAPI();

    APIAdapter() {
        ExternalAPI.Data += ExternalAPI_Data;
        Task.Factory.StartNew(Consume, TaskCreationOptions.LongRunning);
    }

    public void Consume() {
        foreach (var e in this.PendingEvents.GetConsumingEnumerable()) {
            if (this.PendingEvents.IsAddingCompleted) return;
            ProcessData(e.Item1, e.Item2);
        }
    }

    public void RequestData() {
        lock (this.AdapterLock) {
            this.ExternalAPI.SynchronousDataRequest(); 
        }
    }

    private void ExternalAPI_Data(object sender, EventArgs e) {
        this.PendingEvents.Add(Tuple.Create(sender, e));
    }

    private void ProcessData(object sender, EventArgs e) {
        lock (this.AdapterLock) {
            Console.Write("Received event.");
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM