将同步包装器放在异步方法上

时间:2012-02-20 21:22:39

标签: c# events asynchronous synchronous

我正在使用包含异步方法的api与PIN设备集成。例如,其中一个名为GetStatus,它会引发一个DeviceStateChangedEvent,并将状态作为参数传递给它。

我希望有一个非异步的接口,所以当我在我的界面上调用GetStatus时,它实际上会返回状态而不是引发一个事件来将数据传递给我。

我想我可以这样做:

public class MSRDevice
{
    StatusInfo _status;
    bool _stateChangedEventCompleted = false;
    IPAD _ipad; // <-- the device

    public MSRDevice()
    {
        //Initialize device, wire up events, etc.
    }

    public StatusInfo GetStatus()
    {
        _ipad.GetStatus() // <- raises StatusChangedEvent
        while(!_stateChangedEventCompleted);
        _stateChangedEventCompleted = false;
        return _status;
    }

    void StateChangedEvent(object sender, DeviceStateChangeEventArgs e) 
    {
         _status = e.StatusInfo;
    }
}

这是解决这个问题的好方法还是有更好的解决方案?

2 个答案:

答案 0 :(得分:5)

您在示例中所做的事情被称为“busy-waiting”(或“旋转”),这在大多数情况下都是不推荐的,因为它浪费了大量的CPU能力。优选地,您应该使用信号机制(例如WaitHandle类)在感兴趣的事件(在您的情况下为StatusChangedEvent)发生时进行同步:

public class MSRDevice
{
    StatusInfo _status;
    IPAD _ipad; // <-- the device

    private EventWaitHandle waitHandle = new AutoResetEvent(false);

    public MSRDevice()
    {
        //Initialize device, wire up events, etc.
    }

    public StatusInfo GetStatus()
    {
        _ipad.GetStatus() // <- raises StatusChangedEvent asynchronously
        waitHandle.WaitOne(); // <- waits for signal
        return _status;
    }

    void StateChangedEvent(object sender, DeviceStateChangeEventArgs e) 
    {
        _status = e.StatusInfo;
        waitHandle.Set(); // <- sets signal
    }
}

答案 1 :(得分:2)

最佳选择:将其编码为异步。

没有;这是一个热门循环。它将锤击CPU。它也不能保证由于寄存器缓存而退出(特别是在x86上演示这一点很简单)。

如果您需要同步,则应使用类似AutoResetEvent的内容。