使用.Net HttpListener进行多线程处理

pro*_*ick 26 .net c# multithreading httplistener

我有一个听众:

listener = new HttpListener();
listener.Prefixes.Add(@"http://+:8077/");
listener.Start();
listenerThread = new Thread(HandleRequests);
listenerThread.Start();
Run Code Online (Sandbox Code Playgroud)

我正在处理请求:

private void HandleRequests()
{
    while (listener.IsListening)
    {
        var context = listener.BeginGetContext(new AsyncCallback(ListenerCallback), listener);
        context.AsyncWaitHandle.WaitOne();
    }
}

private void ListenerCallback(IAsyncResult ar)
{
    var listener = ar.AsyncState as HttpListener;

    var context = listener.EndGetContext(ar);

    //do some stuff
}
Run Code Online (Sandbox Code Playgroud)

我想void Stop()用这样的方式写:

  1. 它将阻塞,直到所有当前处理的请求结束(即等待所有线程"执行某些操作").
  2. 虽然它将等待已经开始的请求,但它将不允许任何更多的请求(即在开始时返回ListenerCallback).
  3. 之后它将调用listener.Stop()(listener.IsListening变为假).

怎么可能写?

编辑:您对此解决方案有何看法?安全吗?

public void Stop() 
{
    lock (this)
    {
        isStopping = true;
    }
    resetEvent.WaitOne(); //initially set to true
    listener.Stop();
}

private void ListenerCallback(IAsyncResult ar)
{
    lock (this)
    {
        if (isStopping)
            return;

        resetEvent.Reset();
        numberOfRequests++;
    }

    var listener = ar.AsyncState as HttpListener;

    var context = listener.EndGetContext(ar);

    //do some stuff

    lock (this)
    {
        if (--numberOfRequests == 0)
            resetEvent.Set();
    }
}
Run Code Online (Sandbox Code Playgroud)

csh*_*net 61

为了完整性,如果您管理自己的工作线程,它会是什么样子:

class HttpServer : IDisposable
{
    private readonly HttpListener _listener;
    private readonly Thread _listenerThread;
    private readonly Thread[] _workers;
    private readonly ManualResetEvent _stop, _ready;
    private Queue<HttpListenerContext> _queue;

    public HttpServer(int maxThreads)
    {
        _workers = new Thread[maxThreads];
        _queue = new Queue<HttpListenerContext>();
        _stop = new ManualResetEvent(false);
        _ready = new ManualResetEvent(false);
        _listener = new HttpListener();
        _listenerThread = new Thread(HandleRequests);
    }

    public void Start(int port)
    {
        _listener.Prefixes.Add(String.Format(@"http://+:{0}/", port));
        _listener.Start();
        _listenerThread.Start();

        for (int i = 0; i < _workers.Length; i++)
        {
            _workers[i] = new Thread(Worker);
            _workers[i].Start();
        }
    }

    public void Dispose()
    { Stop(); }

    public void Stop()
    {
        _stop.Set();
        _listenerThread.Join();
        foreach (Thread worker in _workers)
            worker.Join();
        _listener.Stop();
    }

    private void HandleRequests()
    {
        while (_listener.IsListening)
        {
            var context = _listener.BeginGetContext(ContextReady, null);

            if (0 == WaitHandle.WaitAny(new[] { _stop, context.AsyncWaitHandle }))
                return;
        }
    }

    private void ContextReady(IAsyncResult ar)
    {
        try
        {
            lock (_queue)
            {
                _queue.Enqueue(_listener.EndGetContext(ar));
                _ready.Set();
            }
        }
        catch { return; }
    }

    private void Worker()
    {
        WaitHandle[] wait = new[] { _ready, _stop };
        while (0 == WaitHandle.WaitAny(wait))
        {
            HttpListenerContext context;
            lock (_queue)
            {
                if (_queue.Count > 0)
                    context = _queue.Dequeue();
                else
                {
                    _ready.Reset();
                    continue;
                }
            }

            try { ProcessRequest(context); }
            catch (Exception e) { Console.Error.WriteLine(e); }
        }
    }

    public event Action<HttpListenerContext> ProcessRequest;
}
Run Code Online (Sandbox Code Playgroud)


pro*_*ick 2

我已经在问题的编辑部分查阅了我的代码,并决定接受它并进行一些修改:

public void Stop() 
{
    lock (locker)
    {
        isStopping = true;
    }
    resetEvent.WaitOne(); //initially set to true
    listener.Stop();
}

private void ListenerCallback(IAsyncResult ar)
{
    lock (locker) //locking on this is a bad idea, but I forget about it before
    {
        if (isStopping)
            return;

        resetEvent.Reset();
        numberOfRequests++;
    }

    try
    {
        var listener = ar.AsyncState as HttpListener;

        var context = listener.EndGetContext(ar);

        //do some stuff
    }
    finally //to make sure that bellow code will be executed
    {
        lock (locker)
        {
            if (--numberOfRequests == 0)
                resetEvent.Set();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)