同步异步事件

时间:2016-04-19 14:43:07

标签: c# events asynchronous .net-4.5

我有一个从外部类接收标准.Net事件的类。 这些事件有一个地址属性(当然还有很多其他属性)我可以用它来同步我的事件,这样我就可以创建一个获取东西的方法,等待正确的事件,然后返回Get方法中事件的数据。

但是,我对C#中的同步还是比较陌生的,并且希望你们中的任何一个能帮助我。下面是我想要完成的伪代码:

  1. 有人致电DoAsynchronousToSynchronousCall
  2. 该方法等待直到收到具有相同地址的事件(或直到它超时)
  3. 该事件会检查所有当前请求。如果它找到具有相同地址的请求,请让DoAsynchronousToSynchronousCall知道回复已到达
  4. DoAsynchronousCall获取(或检索)回复并将其返回给调用者
  5. public class MyMessage
    {
        public string Address { get; set; }
        public string Data { get; set; }
    }
    
    public Main
    {
        externalClass.MessageReceived += MessageReceived;
    }
    
    public void MessageReceived(MyMessage message)
    {
        MyMessage request = _requestQueue.FirstOrDefault(m => m.Address = message.Address);
        if (request != null)
        {
            // Do something to let DoAsynchronousToSynchronousCall() know the reply has arrived
        }
    }
    
    private List<MyMessage> _requestQueue = new List<MyMessage>();
    public MyMessage DoAsynchronousToSynchronousCall(MyMessage message)
    {
        _requestQueue.Add(message);
        externalClass.Send(message);
    
        // Do something to wait for a reply (as checked for above)
        MyMessage reply = WaitForCorrectReply(timeout: 10000);
        return reply;
    }
    

    我觉得我错过了使用asyncawait的机会(但我不知道怎么做),我希望你能够理解我根据上述信息尝试完成的任务。

2 个答案:

答案 0 :(得分:0)

您实际上无法动态拨打多个电话并拥有同步响应。如果您想要多个呼叫的同步响应,那么您也需要同步进行呼叫。

我会考虑使用Microsoft的Reactive Extensions(NuGet“Rx-Main”)来尽可能简化您正在做的事情。 Rx允许您将事件转换为可以查询的值流。

这就是我要做的事。

我首先将收到的消息流定义为IObservable<MyMessage> receivedMessages,如下所示:

receivedMessages =
    Observable
        .FromEvent<MessageReceivedHandler, MyMessage>(
            h => externalClass.MessageReceived += h,
            h => externalClass.MessageReceived -= h);

(你没有提供一个def类,所以我调用了事件委托MessageReceivedHandler。)

现在您可以将DoAsynchronousToSynchronousCall重新定义为:

public IObservable<MyMessage> DoAsynchronousCall(MyMessage message)
{
    return Observable.Create<MyMessage>(o =>
    {
        IObservable<MyMessage> result =
            receivedMessages
                .Where(m => m.Address == message.Address)
                .Take(1);

        IObservable<MyMessage> timeout =
            Observable
                .Timer(TimeSpan.FromSeconds(10.0))
                .Select(x => (MyMessage)null);

        IDisposable subscription =
            Observable
                .Amb(result, timeout)
                .Subscribe(o);

        externalClass.Send(message);

        return subscription;
    });
}

result可观察量是针对当前receivedMessages过滤的message.Address

如果调用时间超过timeoutTimeSpan.FromSeconds(10.0) observable将返回默认值。

最后,subscription使用Observable.Amb(...)来确定resulttimeout中的哪一个首先产生值并订阅该结果。

现在打电话给你,你可以这样做:

DoAsynchronousCall(new MyMessage() { Address = "Foo", Data = "Bar" })
    .Subscribe(response => Console.WriteLine(response.Data));

所以,如果我像这样简单地定义ExternalClass

public class ExternalClass
{
    public event MessageReceivedHandler MessageReceived;
    public void Send(MyMessage message)
    {
        this.MessageReceived(new MyMessage()
        {
            Address = message.Address,
            Data = message.Data + "!"
        });
    }
}

...我在控制台上打印出结果Bar!

如果您要处理大量消息,可以执行以下操作:

var messagesToSend = new List<MyMessage>();

/* populate `messagesToSend` */

var query =
    from message in messagesToSend.ToObservable()
    from response in DoAsynchronousCall(message)
    select new
    {
        message,
        response
    };

query
    .Subscribe(x =>
    {
        /* Do something with each correctly paired
            `x.message` & `x.response`
        */
    });

答案 1 :(得分:-1)

你可能正在寻找ManualResetEvent,它可以作为各种类型的“切换”来切换线程阻塞和非阻塞行为。 DoAsynchronousToSynchronousCallReset然后WaitOne(int timeoutMilliseconds)阻止线程的事件,并且检查到的正确回复的内容将执行Set调用以让线程继续如果正确的事情到来的话。

相关问题