Готовый к работе многопоточный сервер c # http - PullRequest
4 голосов
/ 16 июня 2011

Я реализовал HTTP-сервер в c # .NET:

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 не будет готов для рабочих систем, но я никогда не видел аргумента в поддержку этого утверждения.Это правда или нет?
  • Есть ли другие вещи, которые я должен рассмотреть, о которых я не упомянул?

Спасибо за ваши идеи

1 Ответ

4 голосов
/ 16 июня 2011

Имейте в виду, что я не являюсь экспертом в многопоточности, поэтому вам следует постараться как можно лучше проверить все, что я скажу.

Если кто-то еще знает и хотел бы просто украсть весь мой ответ и просто отредактировать или исправить детали, не стесняйтесь делать это.

Давайте разберемся с вашими вопросами один за другим:

Я добавил несколько замков тут и там, чтобы предотвратить условия гонки, но я запутался: действительно ли они нужны? Я прочитал в документации, что все публичные нестатические методы НЕ являются потокобезопасными. Почему я не видел код, где рассматривается этот факт?

Ну, да, и нет. Блокировки обычно используются для предотвращения одновременного доступа нескольких потоков к одной и той же структуре данных, так как это может повредить структуру данных. Рассмотрите возможность сортировки массива в одном потоке и вставки элемента посередине в другом, так как синхронизация этих двух потоков приведет к повреждению содержимого массива.

Теперь в вашем коде вы блокируете this, что никогда не было хорошей идеей. Внешний код может также блокировать тот же объект, и это вне вашего контроля, поэтому в интересах создания кода, готового к производству , я бы этого не делал.

Если вам нужны блокировки в вашем коде, я бы сконструировал определенные объекты блокировки и использовал бы их.

Другими словами, добавьте это:

private readonly object _Lock = new object();

и затем везде, где у вас есть lock(this), замените его на lock(_Lock). Таким образом, при необходимости вы также можете иметь несколько блокировок.

Что касается фактически нуждающихся в замках, я не уверен на 100% в этом. В чем я не уверен, так это в том, что вы блокируете перед вызовом Stop, и вы блокируете обратный вызов, а внутри блокировки вы проверяете, все ли еще работает слушатель.

Это предотвратит остановку слушателя после принятия запроса, но до того, как вы действительно обработаете запрос. Другими словами, похоже, что вы не допустите закрытия сервера с открытыми запросами, которые все еще обрабатываются.

Но, нет, вы не предотвратили бы это, потому что вы могли бы остановить сервер после выхода из заблокированного раздела в обратном вызове, но во время или до того, как прокомментированный код полностью выполнил, так что у вас все еще будет эта проблема.

Однако Это также будет означать, что вы эффективно сериализовали некоторые методы обратного вызова, часть, где вы вызываете EndGetContext и перезапускаете цикл BeginGetContext. Хорошо это или нет, я не знаю.

Как ведет себя HttpListenerContext? Есть ли у него какая-то связь с HttpListener? Или я могу использовать несколько HttpListenerContexts одновременно?

Здесь я сделаю предположение. Этот класс не имеет ссылки обратно на класс слушателя, или , он имеет поточно-ориентированный способ взаимодействия с ним.

Это не будет большая часть системы прослушивания http на основе потоков, если каждый доступ к данным запроса / ответа должен быть сериализован.

В любом случае, если вы сомневаетесь, проверьте документацию о методах и / или свойствах класса контекста, к которому вы обращаетесь, и если вам необходимо предпринять шаги для обеспечения безопасности потока, документация скажет об этом.

Я слышал, что HttpListener не будет готов к работе с производственными системами, но я никогда не видел аргументов в поддержку этого утверждения. Это правда или нет?

(см. Комментарий к вопросу)

Есть ли другие вещи, которые я должен рассмотреть, о которых я не упомянул?

Многопоточный код трудно для записи. Судя по вашему вопросу, я бы рискнул предположить, что вы не сделали этого много, и, честно говоря, хотя я сделал это много, я все еще чувствую, что я на тонком льду.

Мой совет таков:

  • Вам действительно нужна многопоточность?
  • У вас есть другие коллеги, которые знают об этом больше, и которые могут вам помочь?
...