Как выполнить метод в другом потоке?


Я ищу решение этой проблемы в C или C++.
edit : для уточнения. Это на системе linux. Специфичные для Linux решения абсолютно хороши. Крестообразная форма не вызывает беспокойства.

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

В настоящее время я использую методы-оболочки, которые создают структуру с входом и выходом параметры, вставьте структуру в очередь и либо верните (если" команда " асинхронна), либо дождитесь ее выполнения (если "команда" синхронна).

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

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

Edit-вид вывода:
Похоже, что нет никакого de facto способа реализовать то, что я просил, не требующего дополнительных усилий по кодированию.
Я буду придерживаться того, что я придумал, это обеспечивает безопасность типа, минимизирует блокировку, позволяет синхронизировать и асинхронные вызовы, а накладные расходы довольно скромны.
С другой стороны это требует немного дополнительного кодирования и механизм отправки может стать раздутым по мере увеличения числа методов. Регистрация методов диспетчеризации при построении или использование оболочек, которые выполняют эту работу, кажется, решают проблему, удаляют немного накладных расходов, а также удаляют некоторый код.

5 9

5 ответов:

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

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

Внутри службы регистрируют эти классы методов в момент инициализация. Как только запрос поступает в поток, он будет иметь только два ARG, Input и Ouput, которые являются базовыми классами для более специализированных аргументов, требуемых различными классами методов.

Затем вы записываете свой класс обслуживания как простое делегирование, которое принимает входящий запрос и передает соответствующий класс метода на основе идентификатора или имени метода (используемого при первоначальной регистрации).

Я надеюсь, что это имеет смысл, очень хороший пример такого подхода находится в XmlRpc++ (C++ реализация XmlRpc, вы можете получить исходный код от sourceforge).

Резюмировать:

struct Input {
  virtual ~Input () = 0;
};

struct Ouput {
  virtual ~Output () = 0;
};

struct MethodInterface {
   virtual int32_t execute (Input* __input, Output* __output)  = 0;
};

// Write specialized method classes and taking specialized input, output classes
class MyService {


  void registerMethod (std::string  __method_name, MethodInterface* __method);
  //external i/f
  int32_t execute (std::string __method, Input* __input, Output* __output);
};

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

Моя стандартная ссылка для этой задачи - здесь.

Реализация потокобезопасной очереди с использованием переменных условий

Как отметил @John, это использует Boost.Нить.

Я был бы осторожен с синхронным случаем, который вы описали здесь. Легко получить проблемы perf, если производитель (поток отправки) ожидает результата от потребителя (поток обслуживания). Что произойдет, если вы получите 1000 асинхронных вызовов, заполнив очередь с отставанием, а затем синхронизировать звонки от каждого из ваших потоков-производителей? Ваша система будет "играть мертвой", пока не очистится отставание очереди, освобождая тех, кто вызывает синхронизацию. Попробуйте разъединить их, используя только асинхронность, если это возможно.

ИМХО, Если вы хотите отделить выполнение метода и контекст потока, вы должны использовать активный шаблон объекта (AOP)

Однако вам необходимо использовать платформу ACE, которая поддерживает многие операционные системы, например Windows, Linux, VxWorks

Вы можете найти подробную информацию здесь

Кроме того, AOP-это комбинация команд, прокси и шаблонов наблюдателя, если вы знаете их детали, вы можете реализовать свой собственный AOP. Надеюсь, это поможет

В дополнение к использованию Boost.Нить, я бы посмотрел на boost:: function и boost:: bind. Тем не менее, кажется справедливым, чтобы нетипизированные (void) аргументы передавались целевым методам, и пусть эти методы приводят к правильному типу (типичная идиома для таких языков, как C#).

Эй, Радживджи, по-моему, у тебя все перевернуто вверх ногами. Сложность кода обратно пропорциональна гибкости. Чем сложнее ваши структуры данных и алгоритмы, тем больше ограничений вы накладываете на приемлемые входные данные и поведение.

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

struct Xqt { virtual void xqt(){} virtual ~Xqt(){} };

И затем иметь потокобезопасную очередь из указатели на Xqt. Затем поток службы просто выводит очередь на px и вызывает px - >xqt (), а затем удаляет px. Наиболее важным производным классом является следующий:

  struct Dxqt : Xqt { 
    xqt *delegate; 
    Dxqt(xqt *d) : delegate(d) {}
    void xqt() { delegate->xqt(); }
  };

Потому что "все проблемы в информатике могут быть решены еще одним уровнем косвенности", и в частности этот класс не удаляет делегат. Это намного лучше, чем использовать флаг, например, чтобы определить, должен ли объект закрытия быть удален потоком сервера.