我正在连接到一个 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/