How can I use a method async Task and return string, and then how do I pass a delegate for that method to a constructor and use it later?

前端 未结 4 524
滥情空心
滥情空心 2021-01-20 15:05

I have this method that returns string:

public string SendResponse(HttpListenerRequest request)
{
    string result = \"\";
    string key = req         


        
4条回答
  •  一生所求
    2021-01-20 15:37

    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:

    • As I mentioned in my previous answer, you can synchronously wait on asynchronous operations. Not ideal, really just a stop-gap measure until you can improve the code further, but it can work.
    • You can capture the asynchronous operation (i.e. the 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.

提交回复
热议问题