Ste*_*unn 5 .net c# asynchronous httplistener task-parallel-library
我们有一个使用基于HTTP API .NET应用程序,我们POST
的请求向第三方HTTP端点(这不是我们的控制之下)和它在上,我们给它一个HTTP终结以后叫我们回 ; 就像是:
WebRequest request = WebRequest.Create(urlToMethod);
request.Method = @"POST";
request.Headers.Add(@"Callback", "http://ourserver?id="+id );
Run Code Online (Sandbox Code Playgroud)
我们成千上万的这些调用,所以我们希望尽可能有效(在速度/内存/线程等方面)
就回调代码而言,我们有一个充当监听器的类型; 这就是我们启动它的方式:
_httpListener = new HttpListener();
_httpListener.Prefixes.Add(ourServer);
_httpListener.Start();
_httpListener.BeginGetContext(callback, null);
Run Code Online (Sandbox Code Playgroud)
当服务器回调我们时,它会触及我们的callback
方法,如下所示:
HttpListenerContext context = _httpListener.EndGetContext(result);
HttpListenerResponse httpListenerResponse = context.Response;
httpListenerResponse.StatusCode = 200;
httpListenerResponse.ContentLength64 = _acknowledgementBytes.Length;
var output = httpListenerResponse.OutputStream;
output.Write(_acknowledgementBytes, 0, _acknowledgementBytes.Length);
context.Response.Close();
var handler = ResponseReceived;
if (handler != null)
{
handler(this, someData);
}
Run Code Online (Sandbox Code Playgroud)
因此,我们有一个这个侦听器的实例(_which内部使用HttpListener
),并且对于它获得的每个响应,它都会通知ResponseReceived
事件中的所有订阅者.
订阅者(可能是数百个)只关心与其特定关联的数据id
.本subscriber
来看是这样的:
_matchingResponseReceived = new ManualResetEventSlim(false);
_listener.WhenResponseReceived += checkTheIdOfWhatWeGetAndSetTheEventIfItMatches;
postTheMessage();
_matchingResponseReceived.Wait(someTimeout);
Run Code Online (Sandbox Code Playgroud)
这是困扰我的最后一行.我们发布消息,然后阻止整个线程等待Listener
获取响应并调用我们的事件处理程序.我们想使用Task
s,但如果我们阻止整个线程等待回调,它似乎不会给我们太多.
是否有更好的(更多TPL友好)方式实现这一点,以便没有线程被阻止,我们同时触发更多的请求?
async
-几乎await
所有的东西TaskCompletionSource
都是为此而设计的。
发送方创建一个TaskCompletionSource
,将其添加到字典中(键是请求的 id),发出请求并返回 ' TaskCompletionSource
s Task
。
然后接收者在字典中查找正确的TaskCompletionSource
,将其从那里删除并设置其结果。
发送方方法的调用者将await
返回Task
,这将异步等待接收方处理回调调用。
在代码中,它可能看起来像这样:
\n\n// TODO: this probably needs to be thread-safe\n// you can use ConcurrentDictionary for that\nDictionary<int, TaskCompletionSource<Result>> requestTcses;\n\npublic async Task<Result> MakeApiRequestAsync()\n{\n int id = \xe2\x80\xa6;\n var tcs = new TaskCompletionSource<Result>();\n requestTcses.Add(id, tcs);\n await SendRequestAsync(id);\n return await tcs.Task;\n}\n\n\xe2\x80\xa6\n\nvar result = await MakeApiRequest();\n
Run Code Online (Sandbox Code Playgroud)\n\n\n\nvar context = await _httpListener.GetContext();\n\n// parse the response into id and result\n\nvar tcs = requestTcses[id];\nrequestTcses.Remove(id);\ntcs.SetResult(result);\n
Run Code Online (Sandbox Code Playgroud)\n