I have this method that returns string
:
public string SendResponse(HttpListenerRequest request)
{
string result = \"\";
string key = req
As answerer Yuval says (and as my answer to your previous question said), once you start with async
, it generally has to propagate all the way up the call stack. That said, there are alternatives:
Task
) and use that later.In your specific example, the second option should work just fine. I.e. the first thing you need to fix is simply to adjust your constructor so that it can accept the asynchronous method. Then you can call that method later, preferably asynchronously.
For example:
class WebServer
{
private readonly HttpListener _listener = new HttpListener();
private readonly Func> _responderMethod;
public WebServer(string[] prefixes, Func> method)
{
if (!HttpListener.IsSupported)
throw new NotSupportedException(
"Needs Windows XP SP2, Server 2003 or later.");
// URI prefixes are required, for example
// "http://localhost:8080/index/".
if (prefixes == null || prefixes.Length == 0)
throw new ArgumentException("prefixes");
// A responder method is required
if (method == null)
throw new ArgumentException("method");
foreach (string s in prefixes)
_listener.Prefixes.Add(s);
_responderMethod = method;
_listener.Start();
}
public WebServer(Func> method, params string[] prefixes)
: this(prefixes, method) { }
public void Run()
{
ThreadPool.QueueUserWorkItem((o) =>
{
Console.WriteLine("Webserver running...");
try
{
while (_listener.IsListening)
{
ThreadPool.QueueUserWorkItem(async (c) =>
{
var ctx = c as HttpListenerContext;
try
{
string rstr = await _responderMethod(ctx.Request);
System.Diagnostics.Trace.Write(ctx.Request.QueryString);
//ctx.Request.QueryString
byte[] buf = Encoding.UTF8.GetBytes(rstr);
ctx.Response.ContentLength64 = buf.Length;
ctx.Response.OutputStream.Write(buf, 0, buf.Length);
System.Data.SqlClient.SqlConnectionStringBuilder builder = new System.Data.SqlClient.SqlConnectionStringBuilder();
}
catch { } // suppress any exceptions
finally
{
// always close the stream
ctx.Response.OutputStream.Close();
}
}, _listener.GetContext());
}
}
catch { } // suppress any exceptions
});
}
public void Stop()
{
_listener.Stop();
_listener.Close();
}
}
Of course, with the changed constructor, any other callers (if any exist) will also have to be changed. Ideally, you would go change the code related to those callers so that it also follows the async
model, taking full advantage and gaining the full benefits of that approach.
But again, if you cannot or will not do that, it is possible to adapt older synchronous code to the asynchronous model. E.g. if you have something like this:
var server = new WebServer(SomeOtherSendResponse, "http://+:8098/");
…you could change it to something like this:
var server = new WebServer(
request => Task.Run(() => SomeOtherSendResponse(request)),
"http://+:8098/");
You could alternatively create a constructor overload to wrap the synchronous method for any such callers, so that the original call site can remain the same:
public WebServer(Func method, params string[] prefixes)
: this(request => Task.Run(() => method(request)), prefixes)
{ }
Note that by following async
all the way up the call stack, none of the places in the code where the operation would have to wait for an extended period of time on some asynchronous event or action will cause the code to block. Instead, they simply return control to the executing thread, allowing the framework to continue execution of the async
method later if and when the asynchronous event or action occurs or completes.
Were you to attempt to "solve" the problem by simply waiting synchronously for the asynchronous operation to complete, you would (in this particular case) tie up a thread pool thread, blocking it until it could proceed. This way, the thread pool thread is returned to the pool as soon as the asynchronous operation is started and your program won't require the use of a thread pool thread again for that operation until the operation itself actually completes.
As an aside: it is not recommended as a general rule to have code that will ignore literally every exception that could occur. You should catch only those exceptions that you expect to occur and which you know for sure are safe to ignore. Even there, you should at least report them somehow to aid in yours or a user's ability to diagnose problems that are not in your code. Other exceptions can easily leave your program's execution in a corrupt state. At best, those kinds of problems will be extremely difficult to diagnose and fix and at worst you could wind up corrupting user state, output data, etc.