如何改进和/或模块化我对基于事件的任务的处理?

时间:2022-06-19 03:49:38

So I have a server and I'm making calls to it through a wrapped up WebSocket (WebSocket4Net) and one of the requirements of the library I'm building is the ability to await on the return of the request. So I have a class MessageEventHandler that contains events that are triggered by the class MessageHandler as messages come in.

所以我有一个服务器,我通过一个包装的WebSocket(WebSocket4Net)调用它,我正在构建的库的一个要求就是能够等待请求的返回。所以我有一个MessageEventHandler类,它包含消息进来时由MessageHandler类触发的事件。

MessageEventHandler ex.

public class MessageEventHandler : IMessageEventHandler
    {
        public delegate void NodeNameReceived(string name);

        public event Interfaces.NodeNameReceived OnNodeNameReceived;

        public void NodeNameReceive(string name)
        {
            if (this.OnNodeNameReceived != null)
            {
                this.OnNodeNameReceived(name);
            }
        }
    }

MessageHandler ex.

public class MessageHandler : IMessageHandler
    {
        private IMessageEventHandler eventHandler;

        public MessageHandler(IMessageEventHandler eventHandler)
        {
            this.eventHandler = eventHandler;
        }

        public void ProcessDataCollectorMessage(string message)
        {
            var serviceMessage = JsonConvert.DeserializeObject<ServiceMessage>(message);

            switch (message.MessageType)
            {
                case MessageType.GetNodeName:
                    {
                        var nodeName = serviceMessage.Data as string;

                        if (nodeName != null)
                        {
                            this.eventHandler.NodeNameReceive(nodeName);
                        }

                        break;
                    }
                default:
                {
                    throw new NotImplementedException();
                }
             }
         }

Now building upon those classes I have the class containing my asynchronous function that handles the call to get the node name.

现在构建在这些类上,我有一个包含我的异步函数的类,它处理调用以获取节点名称。

    public class ClientServiceInterface : IClientServiceInterface
    {
        public delegate void RequestReady(ServiceMessage serviceMessage);

        public event Interfaces.RequestReady OnRequestReady;

        public int ResponseTimeout { get; private set; }

        private IMessageEventHandler messageEventHandler;

        public ClientServiceInterface(IMessageEventHandler messageEventHandler, int responseTimeout = 5000)
        {
            this.messageEventHandler = messageEventHandler;
            this.ResponseTimeout = responseTimeout;
        }

        public Task<string> GetNodeNameAsync()
        {
            var taskCompletionSource = new TaskCompletionSource<string>();

            var setHandler = default(NodeNameReceived);
            setHandler = name =>
            {
                taskCompletionSource.SetResult(name);
                this.messageEventHandler.OnNodeNameReceived -= setHandler;
            };

            this.messageEventHandler.OnNodeNameReceived += setHandler;

            var ct = new CancellationTokenSource(this.ResponseTimeout);

            var registration = new CancellationTokenRegistration();
            registration = ct.Token.Register(
                () =>
                    {
                        taskCompletionSource.TrySetCanceled();
                        this.messageEventHandler.OnNodeNameReceived -= setHandler;
                        registration.Dispose();
                    },
                false);

            var serviceMessage = new ServiceMessage() { Type = MessageType.GetNodeName };
            this.ReadyMessage(serviceMessage);

            return taskCompletionSource.Task;
        }
    }

As you can see I wouldn't call it pretty and I apologize if anyone threw up a little reading it. But this is my first attempt at wrapping a Task with Asynchronous Event. So with that on the table I could use some help.

正如你所看到的,我不会把它称之为漂亮,如果有人把它读出来,我会道歉。但这是我第一次尝试用异步事件包装一个Task。所以在桌面上我可以使用一些帮助。

Is there a better way to accomplish what I'm trying to achieve here? Remembering that I want a user of the library to either subscribe to the event and listen for all callbacks OR they can simply await the return depending on their needs.

有没有更好的方法来实现我在这里想要实现的目标?记住我希望库的用户订阅事件并监听所有回调,或者他们可以根据需要等待返回。

var nodeName = await GetNodeNameAsync();
Console.WriteLine(nodeName);

or

messageEventHandler.OnNodeNameReceived += (name) => Console.WriteLine(name);
GetNodeNameAsync();

Alternatively if my approach is actually 'good' can anyone provide any advice as to how I can write a helper function to abstract out setting up each function in this way? Any help would be greatly appreciated.

或者,如果我的方法实际上是“好的”,任何人都可以提供任何建议,如何编写辅助函数来抽象出以这种方式设置每个函数?任何帮助将不胜感激。

2 个解决方案

#1


So I've written a couple classes to solve the problem I was having. The first of which is my CallbackHandle class which contains the task inside the TaskCompletionSource so each time that a request is made in my example a new callback handle is created.

所以我写了几个课来解决我遇到的问题。第一个是我的CallbackHandle类,它包含TaskCompletionSource中的任务,因此每次在我的示例中发出请求时,都会创建一个新的回调句柄。

public class CallbackHandle<T>
{
    public CallbackHandle(int timeout)
    {
        this.TaskCompletionSource = new TaskCompletionSource<T>();

        var cts = new CancellationTokenSource(timeout);

        cts.Token.Register(
            () =>
                {
                    if (this.Cancelled != null)
                    {
                        this.Cancelled();
                    }
                });

        this.CancellationToken = cts;
    }

    public event Action Cancelled;

    public CancellationTokenSource CancellationToken { get; private set; }

    public TaskCompletionSource<T> TaskCompletionSource { get; private set; }
}

Then I have a 'handler' that manages the handles and their creation.

然后我有一个“处理程序”来管理句柄及其创建。

public class CallbackHandler<T>
{
    private readonly IList<CallbackHandle<T>> callbackHandles;

    private readonly object locker = new object();

    public CallbackHandler()
    {
        this.callbackHandles = new List<CallbackHandle<T>>();
    }

    public CallbackHandle<T> AddCallback(int timeout)
    {
        var callback = new CallbackHandle<T>(timeout);

        callback.Cancelled += () =>
            {
                this.callbackHandles.Remove(callback);
                callback.TaskCompletionSource.TrySetResult("Error");
            };

        lock (this.locker)
        {
            this.callbackHandles.Add(callback);
        }

        return callback;
    }

    public void EventTriggered(T eventArgs)
    {
        lock (this.locker)
        {
            if (this.callbackHandles.Count > 0)
            {
                CallbackHandle<T> callback =
                    this.callbackHandles.First();

                if (callback != null)
                {
                    this.callbackHandles.Remove(callback);

                    callback.TaskCompletionSource.SetResult(eventArgs);
                }
            }
        }
    }
}

This is a simplified version of my actual implementation but it should get someone started if they need something similar. So to use this on my ClientServiceInterface class in my example I would start by creating a class level handler and using it like this:

这是我实际实现的简化版本,但如果他们需要类似的东西,它应该让某人启动。因此,在我的示例中,在我的ClientServiceInterface类中使用它,我将首先创建一个类级别处理程序并使用它,如下所示:

public class ClientServiceInterface : IClientServiceInterface
{
private readonly CallbackHandler<string> getNodeNameHandler;

public ClientServiceInterface(IMessageEventHandler messageEventHandler, int responseTimeout = 5000)
    {
        this.messageEventHandler = messageEventHandler;
        this.ResponseTimeout = responseTimeout;

        this.getNodeNameHandler = new
CallbackHandler<string>();

        this.messageEventHandler.OnNodeNameReceived += args => this.getNodeNameHandler.EventTriggered(args);
    }

public Task<string> GetNodeNameAsync()
    {
        CallbackHandle<string> callbackHandle = this.getNodeNameHandler.AddCallback(this.ResponseTimeout);

        var serviceMessage = new ServiceMessage
                                 {
                                     Type = MessageType.GetNodeName.ToString()
                                 };
        this.ReadyMessage(serviceMessage);

        return callbackHandle.TaskCompletionSource.Task;
    }

// Rest of class declaration removed for brevity
}

Which is much better looking than what I had before (at least in my opinion) and it's easy to extend.

这比以前更好看(至少在我看来),并且很容易扩展。

#2


For starters follow a thread-safe pattern:

对于初学者,请遵循线程安全模式:

    public void NodeNameReceive(string name)
    {
        var evt = this.OnNodeNameReceived;
        if (evt  != null)
        {
            evt (name);
        }
    }

If you do not take a reference to the event object it can be set to null between the time you check null and call the method.

如果不引用事件对象,则可以在检查null和调用方法之间将其设置为null。

#1


So I've written a couple classes to solve the problem I was having. The first of which is my CallbackHandle class which contains the task inside the TaskCompletionSource so each time that a request is made in my example a new callback handle is created.

所以我写了几个课来解决我遇到的问题。第一个是我的CallbackHandle类,它包含TaskCompletionSource中的任务,因此每次在我的示例中发出请求时,都会创建一个新的回调句柄。

public class CallbackHandle<T>
{
    public CallbackHandle(int timeout)
    {
        this.TaskCompletionSource = new TaskCompletionSource<T>();

        var cts = new CancellationTokenSource(timeout);

        cts.Token.Register(
            () =>
                {
                    if (this.Cancelled != null)
                    {
                        this.Cancelled();
                    }
                });

        this.CancellationToken = cts;
    }

    public event Action Cancelled;

    public CancellationTokenSource CancellationToken { get; private set; }

    public TaskCompletionSource<T> TaskCompletionSource { get; private set; }
}

Then I have a 'handler' that manages the handles and their creation.

然后我有一个“处理程序”来管理句柄及其创建。

public class CallbackHandler<T>
{
    private readonly IList<CallbackHandle<T>> callbackHandles;

    private readonly object locker = new object();

    public CallbackHandler()
    {
        this.callbackHandles = new List<CallbackHandle<T>>();
    }

    public CallbackHandle<T> AddCallback(int timeout)
    {
        var callback = new CallbackHandle<T>(timeout);

        callback.Cancelled += () =>
            {
                this.callbackHandles.Remove(callback);
                callback.TaskCompletionSource.TrySetResult("Error");
            };

        lock (this.locker)
        {
            this.callbackHandles.Add(callback);
        }

        return callback;
    }

    public void EventTriggered(T eventArgs)
    {
        lock (this.locker)
        {
            if (this.callbackHandles.Count > 0)
            {
                CallbackHandle<T> callback =
                    this.callbackHandles.First();

                if (callback != null)
                {
                    this.callbackHandles.Remove(callback);

                    callback.TaskCompletionSource.SetResult(eventArgs);
                }
            }
        }
    }
}

This is a simplified version of my actual implementation but it should get someone started if they need something similar. So to use this on my ClientServiceInterface class in my example I would start by creating a class level handler and using it like this:

这是我实际实现的简化版本,但如果他们需要类似的东西,它应该让某人启动。因此,在我的示例中,在我的ClientServiceInterface类中使用它,我将首先创建一个类级别处理程序并使用它,如下所示:

public class ClientServiceInterface : IClientServiceInterface
{
private readonly CallbackHandler<string> getNodeNameHandler;

public ClientServiceInterface(IMessageEventHandler messageEventHandler, int responseTimeout = 5000)
    {
        this.messageEventHandler = messageEventHandler;
        this.ResponseTimeout = responseTimeout;

        this.getNodeNameHandler = new
CallbackHandler<string>();

        this.messageEventHandler.OnNodeNameReceived += args => this.getNodeNameHandler.EventTriggered(args);
    }

public Task<string> GetNodeNameAsync()
    {
        CallbackHandle<string> callbackHandle = this.getNodeNameHandler.AddCallback(this.ResponseTimeout);

        var serviceMessage = new ServiceMessage
                                 {
                                     Type = MessageType.GetNodeName.ToString()
                                 };
        this.ReadyMessage(serviceMessage);

        return callbackHandle.TaskCompletionSource.Task;
    }

// Rest of class declaration removed for brevity
}

Which is much better looking than what I had before (at least in my opinion) and it's easy to extend.

这比以前更好看(至少在我看来),并且很容易扩展。

#2


For starters follow a thread-safe pattern:

对于初学者,请遵循线程安全模式:

    public void NodeNameReceive(string name)
    {
        var evt = this.OnNodeNameReceived;
        if (evt  != null)
        {
            evt (name);
        }
    }

If you do not take a reference to the event object it can be set to null between the time you check null and call the method.

如果不引用事件对象,则可以在检查null和调用方法之间将其设置为null。