РЕДАКТИРОВАТЬ: Вы говорите, что не хотите «взломать функцию в до/после ожидания».
На каких языках вы разрабатываете? Если у него есть продолжения (yield return
в C#), то это дает возможность написать код, который выглядит процедурным, но который можно легко приостановить до тех пор, пока операция блокировки не завершит обратный вызов завершения.
Вот статья об идее: http://msdn.microsoft.com/en-us/magazine/cc546608.aspx
UPDATE:
Unfortunatly, язык C++
Это сделало бы большую футболку лозунг.
Хорошо, так что вам может показаться полезным структурировать ваш последовательный код в качестве государственной машины, чтобы он стал прерывать/возобновлять работу.
например. Ваша боль необходимости написать две функции, тот, который инициирует и тот, который действует в качестве обработчика для события завершения:
void send_greeting(const std::string &msg)
{
std::cout << "Sending the greeting" << std::endl;
begin_sending_string_somehow(msg, greeting_sent_okay);
}
void greeting_sent_okay()
{
std::cout << "Greeting has been sent successfully." << std::endl;
}
Ваша идея была ждать:
void send_greeting(const std::string &msg)
{
std::cout << "Sending the greeting" << std::endl;
waiter w;
begin_sending_string_somehow(msg, w);
w.wait_for_completion();
std::cout << "Greeting has been sent successfully." << std::endl;
}
В этом примере, waiter
перегружает operator(), поэтому он может служить обратным вызовом, и wait_for_completion
как-то зависает, пока не увидит, что вызван оператор().
Я предполагаю, что второй параметр begin_sending_string_somehow
является параметром шаблона, который может быть любым вызываемым типом, не принимающим никаких параметров.
Но, как вы говорите, у этого есть недостатки. В любой момент, когда поток ожидает такого, вы добавили еще один потенциальный тупик, и вы также потребляете «ресурс» всего потока и его стека, что означает, что больше потоков потребуется создать в другом месте, чтобы можно было выполнить работу , что противоречит всей точке пула потоков.
Так вместо этого, написать класс:
class send_greeting
{
int state_;
std::string msg_;
public:
send_greeting(const std::string &msg)
: state_(0), msg_(msg) {}
void operator()
{
switch (state_++)
{
case 0:
std::cout << "Sending the greeting" << std::endl;
begin_sending_string_somehow(msg, *this);
break;
case 1:
std::cout << "Greeting has been sent successfully."
<< std::endl;
break;
}
}
};
Класс реализует оператор вызова функции ()
. Каждый раз, когда он вызывается, он выполняет следующий шаг в логике. (Конечно, являясь таким тривиальным примером, теперь это в основном шум управления состоянием, но в более сложном примере с четырьмя или пятью состояниями это может помочь прояснить последовательный характер кода).
Проблема:
Если событие функции обратного вызова подписи есть специальные параметры, вам необходимо добавить еще одну перегрузки operator()
, которая хранит параметры в дополнительных полях, а затем вызывает на перегрузку без параметров. Затем он начинает запутываться, потому что эти поля будут доступны во время компиляции в исходном состоянии, даже если они не имеют смысла во время выполнения в этом состоянии.
Как объекты класса построены и удалены? Объект должен выжить до тех пор, пока операция не завершится или не будет прекращена ... центральная ошибка C++. Я бы рекомендовал реализовать общую схему управления ею. Создайте список «вещей, которые нужно удалить» и убедитесь, что это происходит автоматически в определенных безопасных точках, т. Е. Попытайтесь как можно ближе подойти к GC. Чем дальше вы от этого, тем больше у вас будет утечки.
Unfortunatly, язык C++: P –
Спасибо за подробный ответ. Это интересная техника, однако я не уверен, что она соответствует моей конкретной проблеме. Смотрите, одно из требований, как я уже упоминал, является прозрачностью, поэтому send_greeting должен выглядеть так (псевдо): send_grt (msg) {...; send_string (MSG); сообщение cout << "отправлено";}, поэтому ожидающая часть находится внутри send_string, а не send_greeting. Ваше решение работает, если код написан как конечный автомат на нижнем уровне, в то время как я ищу решение верхнего уровня. Во всяком случае, хорошая идея, и я уверен, что буду использовать ее в другом месте. –
Но тогда вы просто мечтаете о невозможном. Если вы * требуете *, чтобы он выглядел как обычная синхронная функция для вызывающего, то вы * требовали * техники ожидания точно так же, как вы ее первоначально описали, или как показано в моем примере с гипотетическим классом «официант». Это почти все, что нужно. –