c# - 使异步 API 同步

标签 c# asynchronous

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

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

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

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

我想创建一个同步的包装器。这样做最自然的方法是什么?有没有一种聪明的方法来使用异步等待,而不是使用线程锁定之类的东西?

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

最佳答案

没有测试下面的代码,但它应该给你想法。基本上,您可以使用 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();
            }
        }
    }

关于c# - 使异步 API 同步,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39916454/

相关文章:

c# - 如何让 BackgroundWorker 返回一个对象

android - 以 3.0+ 为目标时,在可运行对象中获取 NetworkOnMainThreadException

javascript - 异步剪贴板 API - 未触发 clipboardchange 事件

c# - 可能是内存泄漏或者?

c# - 从 DataGrid 中删除重复行

c# - 从 Linqpad 调用 OData 服务操作

c# - Ajax.BeginForm beforeSubmit 事件

c# - 使用 C# 命令行通过 AzCopy 复制容器

java - Java 是否可以在一个线程中并行运行多个任务?

angularjs - 如何防止浏览器窗口在 AngularJS 中关闭,直到 promise 得到解决