多线程回答HttpListener

Ale*_*lex 6 c# multithreading

我有单线程进程,执行了很长时间.我需要几个用户才能访问执行此过程,我选择http协议来管理调用.

当然,当一个过程正在运行时,其他人应该等到它完成.如果进程可用,则执行.如果没有,则发送BUSY应答.

这是实施:

using System;
using System.Net;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;

namespace simplehttp
{
    class Program
    {
        private static System.AsyncCallback task;
        private static System.Threading.ManualResetEvent mre = new System.Threading.ManualResetEvent(false);// Notifies one or more waiting threads that an event has occurred. 
        private static HttpListenerContext workingContext = null;

        public static bool isBackgroundWorking()
        {
            return mre.WaitOne(0);
        }
        static void Main(string[] args)
        {
            new Thread(() =>
            {
                Thread.CurrentThread.IsBackground = true;
                while (true)
                {
                    Console.WriteLine("    waitOne " + isBackgroundWorking());
                    mre.WaitOne(); // Blocks the current thread until the current WaitHandle receives a signal.
                    Console.WriteLine("    do job" + " [" + Thread.CurrentThread.Name + ":" + Thread.CurrentThread.ManagedThreadId + " ]\n");
                    HttpListenerRequest request = workingContext.Request;
                    HttpListenerResponse response = workingContext.Response;
                    string responseString = "WORK " + DateTime.Now ;
                    byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
                    response.ContentLength64 = buffer.Length;
                    System.IO.Stream output = response.OutputStream;
                    Thread.Sleep(10000);
                    output.Write(buffer, 0, buffer.Length);
                    output.Close();
                    Console.WriteLine("    " + responseString + "\t" + DateTime.Now);
                    workingContext = null;
                    mre.Reset(); // Sets the state of the event to nonsignaled, causing threads to block.
                }
            }).Start();

            // Create a listener.
            HttpListener listener = new HttpListener();

            listener.Prefixes.Add("http://localhost:6789/index/");
            listener.Start();
            Console.WriteLine("Listening..." + " [" + Thread.CurrentThread.Name + ":" + Thread.CurrentThread.ManagedThreadId + " ]\n");

            task = new AsyncCallback(ListenerCallback);

            IAsyncResult resultM = listener.BeginGetContext(task,listener);
            Console.WriteLine("Waiting for request to be processed asyncronously.");

            Console.ReadKey();
            Console.WriteLine("Request processed asyncronously.");
            listener.Close();
        }

        private static void ListenerCallback(IAsyncResult result)
        {
            HttpListener listener = (HttpListener) result.AsyncState;

            //If not listening return immediately as this method is called one last time after Close()
            if (!listener.IsListening)
                return;

            HttpListenerContext context = listener.EndGetContext(result);
            listener.BeginGetContext(task, listener);

            if (workingContext == null && !isBackgroundWorking())
            {
                // Background work
                workingContext = context;
                mre.Set(); //Sets the state of the event to signaled, allowing one or more waiting threads to proceed.
            }
            else
            {
            HttpListenerRequest request = context.Request;
            HttpListenerResponse response = context.Response;
            string responseString = "BUSY "+ DateTime.Now + " [" + Thread.CurrentThread.Name + ":" + Thread.CurrentThread.ManagedThreadId;
            byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
            response.ContentLength64 = buffer.Length;
            System.IO.Stream output = response.OutputStream;
            output.Write(buffer, 0, buffer.Length);
            output.Close();
            Console.WriteLine(responseString + "\t" + DateTime.Now);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

为了测试我做2个http调用.我希望有2个不同的答案WORK和BUSY.但是我看到第二个请求先等待完成然后执行.

      waitOne False
Listening... [:10 ]

Waiting for request to be processed asyncronously.
      do job [:11 ]

      WORK 1/24/2016 10:34:01 AM  1/24/2016 10:34:11 AM
      waitOne False
      do job [:11 ]

      WORK 1/24/2016 10:34:11 AM  1/24/2016 10:34:21 AM
      waitOne False
Run Code Online (Sandbox Code Playgroud)

我理解它应该如何运作有什么问题?

更新(太多评论没有被SO鼓励):我的代码看起来很尴尬,因为它是真实进程的复制.在"我的"应用程序中,工作过程是主要过程,在某些特定时刻运行嵌入式C#代码具有"礼貌".因此,我无法运行新任务来处理请求,并且它必须是异步的,因为工作流程完成自己的工作,并且只调用从属代码片段以在数据可用时通知客户端.它是异步的,因为代码被调用并且应该尽快完成,否则它将阻止主应用程序.我将尝试添加其他线程与同步调用,并看到它影响情况.

此示例中未使用调试器来干扰打印到控制台的实时进程和时间戳.调试很棒且必要但在这种情况下我尝试用输出替换以避免同步/等待场景中的额外actor.

应用程序本身并不是繁重的对话.1-3个客户很少向主要申请人询问答案.http协议用于方便而不是繁重或经常对话.看起来像IE这样的浏览器工作正常(Windows到Windows会话?),有些像Chrome(更多系统不可知)复制我的应用程序行为.看看时间戳,Chrome,IE,IE,Chrome和最后一个Chrome仍然进入了WORK流程.顺便说一句,每个会话建议更改代码,现在在检索前一个请求后立即发出新请求.

    HttpListenerContext context = listener.EndGetContext(result);
    listener.BeginGetContext(task, listener); 
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

另外,根据建议,我改变了对syncroniuos的异步调用,结果仍然相同

private static void ListenerCallback(IAsyncResult result)
{
    HttpListener listener = (HttpListener) result.AsyncState;

    //If not listening return immediately as this method is called one last time after Close()
    if (!listener.IsListening)
        return;

    HttpListenerContext context = listener.EndGetContext(result);

    while (true)
    {
        if (workingContext == null && !isBackgroundWorking())
        {
            // Background work
            workingContext = context;
            mre.Set(); //Sets the state of the event to signaled, allowing one or more waiting threads to proceed.
        }
        else
        {
            HttpListenerRequest request = context.Request;
            HttpListenerResponse response = context.Response;
            string responseString = "BUSY " + DateTime.Now + " [" + Thread.CurrentThread.Name + ":" +
                                    Thread.CurrentThread.ManagedThreadId;
            byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
            response.ContentLength64 = buffer.Length;
            System.IO.Stream output = response.OutputStream;
            output.Write(buffer, 0, buffer.Length);
            output.Close();
            Console.WriteLine(responseString + "\t" + DateTime.Now);
        }
        context=listener.GetContext();
    }
}
Run Code Online (Sandbox Code Playgroud)

usr*_*usr 3

我还没有完全阅读和理解该代码。它的结构很尴尬。这是一个更清晰的结构,很容易正确:

while (true) {
 var ctx = GetContext();
 Task.Run(() => ProcessRequest(ctx));
}
Run Code Online (Sandbox Code Playgroud)

这只是分派所有传入的工作。然后:

object @lock = new object(); //is work being done?
void ProcessRequest(Context ctx) {
 if (!Monitor.Enter(@lock))
  SendBusy();
 else {
  ProcessInner(ctx); //actually do some work
  Monitor.Exit(@lock);
 }
}
Run Code Online (Sandbox Code Playgroud)

这确实是所需要的一切。

特别是使用异步 IO 是没有意义的。我假设您已经从某处复制了该代码或想法。常见的错误。异步 IO 在这里对您没有任何帮助,并且使代码变得复杂。