使异步API同步

时间:2016-10-07 11:45:07

标签: c# asynchronous

我正在连接API以获取一些定义如下的数据:

客户端对象ClientConnection,允许用户发送请求。 需要传递给ClientConnection以接收回调的IApi接口。 示意性地看起来像这样:

// defined in the API dll
public class ClientConnection {
    public ClientConnection(IApi api) { ... }
    public void request(int reqid, string reqdetails) { ... }
}

interface IApi
{
    void receiveData(int reqid, string ans);
}

现在,显然这是一种相当标准的异步处理方式:通过全局对象发送请求,带有请求,并接收用该请求标记的答案。

我想创建一个同步的包装器。这样做最自然的方式是什么?是否有一种使用异步等待的智能方法,而不是使用线程锁定和东西?

class MyWrapper : IApi
{
    private ClientConnection _client;
    private int _reqToken = 0;
    public MyWrapper()
    {
        _client = new ClientConnection(this);
    }

    public string getData(string reqdetails)
    {
        _client.request(_reqToken++, reqdetails);
        // what to do here?
    }

    public void receiveData(int reqid, string data) {
        // what to do here?
    }
}

1 个答案:

答案 0 :(得分:1)

没有测试下面的代码,但它应该给你这个想法。基本上,当您收到结果时,您可以使用ManualResetEvent发出信号(并且在没有适当超时的情况下不会调用此信号):

    class MyWrapper : IApi {
        private ClientConnection _client;
        // here you store your requests
        private Dictionary<int, PendingRequest> _pendingRequests = new Dictionary<int, PendingRequest>();
        private int _reqToken = 0;

        public MyWrapper() {
            _client = new ClientConnection(this);
        }

        public string getData(string reqdetails, TimeSpan timout) {
            // if this is multithreaded - lock over _pendingRequests when you add\remove requests there
            // and when you increment your _reqToken, or use concurrent collection
            using (var token = new PendingRequest()) {
                var id = _reqToken;
                // lock here
                _pendingRequests.Add(id, token);
                _client.request(id, reqdetails);
                // and here use Interlocked.Increment
                _reqToken++;
                if (!token.Signal.WaitOne(timout)) {
                    // and here
                    _pendingRequests.Remove(id);
                    // timeout
                    throw new Exception("timout");
                }
                // if we are here - we have the result
                return token.Result;
            }
        }

        public void receiveData(int reqid, string data) {
            // here you might need to lock too
            if (_pendingRequests.ContainsKey(reqid)) {                    
                var token = _pendingRequests[reqid];
                _pendingRequests.Remove(reqid);
                token.Complete(data);
            }
        }

        private class PendingRequest : IDisposable {
            public PendingRequest() {
                Signal = new ManualResetEvent(false);
            }

            public ManualResetEvent Signal { get; private set; }

            public string Result { get; private set; }

            public void Complete(string result) {
                this.Result = result;
                Signal.Set();
            }

            public void Dispose() {
                Signal.Dispose();
            }
        }
    }