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

时间:2015-05-06 23:54:20

标签: c# events asynchronous

所以我有一台服务器,我通过一个包装的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();
                }
             }
         }

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

    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;
        }
    }

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

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

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

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

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

2 个答案:

答案 0 :(得分:0)

所以我写了几个课来解决我遇到的问题。第一个是我的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; }
}

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

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

这是我实际实现的简化版本,但如果他们需要类似的东西,应该让某人启动。因此,在我的示例中,在我的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
}

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

答案 1 :(得分:-1)

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

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

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