【问题标题】:Production ready, multi-threaded c# http server生产就绪,多线程 c# http 服务器
【发布时间】:2011-09-16 08:04:47
【问题描述】:

我在 c# .NET 中实现了一个 HTTP 服务器:

public class HttpServer
{
    private HttpListener listener;

    public HttpServer()
    {
        listener = new HttpListener();
        listener.Prefixes.Add("http://localhost:8080/");
    }

    public void Start()
    {
        lock(this) {
            listener.Start();
            AsyncProcessing(listener);
        }
    }

    public void Stop()
    {
        lock (this) {
            listener.Stop();
        }
    }

    private void AsyncProcessing(HttpListener listener)
    {
        if (listener == null)
            return;

        listener.BeginGetContext(new AsyncCallback(Callback), listener);
    }

    private void Callback(IAsyncResult result)
    {
        HttpListenerContext context = null;

        lock(this) {
            HttpListener listener = (HttpListener)result.AsyncState;
            if (!listener.IsListening)
                return;

            context = listener.EndGetContext(result);

            AsyncProcessing(listener);
        }

        /* handle request */
    }
}

我对此实现有一些疑问:

  • 我在这里和那里添加了一些锁来防止竞争条件,但我很困惑:真的需要这些吗?我在文档中读到所有公共非静态方法都不是线程安全的。为什么我没有看到考虑到这一事实的代码?
  • HttpListenerContext 的行为如何?他是否与 HttpListener 有某种联系?或者我可以同时使用多个 HttpListenerContexts 吗?
  • 我听说 HttpListener 不会为生产系统做好准备,但我从未见过支持这种说法的论据。是真是假?
  • 还有其他我没有提到的事情需要考虑吗?

感谢您的想法

【问题讨论】:

  • 静态或非静态方法没有内置的线程安全性,您必须自己提供所有这些。通常,代码不会被编写成完全线程安全的,因为这通常需要多于这里和那里的几个锁。换句话说,类和/或方法是否为静态与代码的线程安全无关。
  • msdn 明确表示公共静态成员是线程安全的。但这并不重要,因为我不使用任何静态成员..
  • 您对问题的“未准备好生产”部分有引用或引用吗?我似乎从未见过这样的评论,并且有兴趣阅读其背后的推理。请注意,这不是我说那句话是错误的,我是说我没听说过,想知道更多。
  • 好的,我的第一条评论是关于静态和非静态方法一般,一个类可能会记录所有静态方法都是线程安全的,但正如我的评论所说,他们将其内置到方法中,使它们成为静态与它无关。这是 Microsoft 的设计和实施选择。
  • @thread-safety:好的,很高兴知道。 @httplistener:是的,我也想了解更多。不幸的是,我不记得我在哪里读到这些 cmets。

标签: c# httplistener


【解决方案1】:

请记住,我不是多线程方面的专家,因此您应该注意尽可能地验证我所说的任何内容。

如果其他人知道,并且想窃取我的整个答案并编辑或更正详细信息,请随时这样做。

让我们一一处理您的问题:

我在这里和那里添加了一些锁来防止竞争条件,但我很困惑:这些真的需要吗?我在文档中读到所有公共非静态方法都不是线程安全的。为什么我没有看到考虑到这一事实的代码?

嗯,是的,也不是。锁通常用于防止多个线程同时访问同一个数据结构,因为它会破坏数据结构。考虑在一个线程上对数组进行排序,并在另一个线程的中间插入一个元素,这两个线程的正确时间会破坏数组的内容。

现在,在您的代码中,您锁定了this,这绝不是一个好主意。外部代码可能也会锁定同一个对象,这是你无法控制的,所以为了创建生产就绪代码,我不会那样做。

如果您的代码中需要锁,我会构造特定的锁对象并使用它们。

换句话说,添加这个:

private readonly object _Lock = new object();

然后在您拥有lock(this) 的任何地方将其替换为lock(_Lock)。这样,如果需要,您还可以拥有多个锁。

至于实际需要锁,我对此不是 100% 确定的。我不确定的事情是你在调用 Stop 之前锁定,并且你锁定在回调和锁定中,你检查侦听器是否仍在运行。

这将阻止您在接受请求后但在您实际处理请求之前停止侦听器。换句话说,听起来您会阻止关闭服务器,而打开的请求仍在处理中。

但是,不,您不会阻止这种情况,因为您可能会在将锁定部分留在回调中之后停止服务器,但在注释代码完全执行期间或之前,您仍然会遇到这个问题。

然而这也意味着您已经有效地序列化了一些回调方法,即调用 EndGetContext 并重新启动 BeginGetContext 循环的部分。这是否是一个好的模式,我不知道。

HttpListenerContext 的行为如何?他是否与 HttpListener 有某种联系?或者我可以同时使用多个 HttpListenerContexts 吗?

在这里,我将做一个猜测。该类没有对侦听器类的引用,,它具有与之合作的线程安全方式。

如果对请求/响应数据的每次访问都必须序列化,那么基于线程的 http 侦听器系统就不会太多了。

无论如何,如果有疑问,请检查您正在访问的上下文类的方法和/或属性的文档,如果您需要采取措施确保线程安全,文档会说明。

我听说 HttpListener 不会为生产系统做好准备,但我从未见过支持这种说法的论据。是真是假?

(见问题评论)

还有其他我没有提到的事情我应该考虑吗?

多线程代码很难编写。从你的问题来看,我冒昧地猜测你并没有做很多事情,老实说,虽然我做了很多事情,但我仍然觉得自己如履薄冰。

我的建议如下:

  • 真的需要多线程吗?
  • 您有其他了解更多这方面的同事可以帮助您吗?

【讨论】:

  • 重新阅读我的答案后,我觉得我实际上并没有回答很多问题,更像是一种自以为是的想法。随意标记为“不是答案”。如果有人想盗用一些文本以获得更好的答案,我现在就离开它。
  • 问题是,我只了解多线程编码背后的概念。我没有任何在 c# 中这样做的经验。所以我阅读了 HttpListener MSDN 并意识到没有关于这个主题的 cmets。这就是为什么我开始怀疑并想把事情弄清楚的原因。然后我插入了一些锁来保护侦听器对象,因为它不是线程安全的(好提示顺便说一句@private 锁对象,谢谢)。我认为如果我在调用 Stop 方法时对侦听器对象进行操作真的很糟糕 :)
  • 我也猜测上下文不依赖于侦听器,这就是请求处理未锁定的原因。但是恕我直言,不知道到底发生了什么有点可怕,MSDN 根本不谈论这个。此外,如果上下文和侦听器不相互依赖,那么停止侦听器并处理剩余的请求不会有任何问题,对吧?那么,您的评论可能自相矛盾,还是我误读了您?
  • 不,你说的完全正确,我必须承认,如果你在请求仍在进行中的情况下阻止听众,我真的不知道会发生什么。我敢打赌,如果您将那部分作为自己的问题提出,您可能会得到比我上面的意见更好的答案。此外,对于未来,通过一次处理一件事来拆分问题可能是明智之举,这通常会在 SO 上得到更好、更合格的答案。
猜你喜欢
  • 1970-01-01
  • 2011-05-15
  • 1970-01-01
  • 2010-10-24
  • 1970-01-01
  • 2011-08-06
  • 2020-04-30
  • 2011-03-11
  • 2011-02-06
相关资源
最近更新 更多