Жизненные проблемы std :: обещать в асинхронном API

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

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

Кандидат API может быть:

std::future<void> sendMessage(Message msg, id expected)
{
// Write the message
auto promise = make_shared<std::promise<void>>();
// Memorize the promise somewhere accessible to the receiving thread
return promise->get_future();
}

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

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

1

Решение

Этот ответ был переписан.

Установка состояния общего флага может позволить работнику узнать, ожидает ли другая сторона, скажем, босса, результата.

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

Одновременное чтение / запись на флаге, вероятно, должно быть синхронизировано.

Босс может не получить доступ к обещанию, а работник не может получить доступ к будущему.

Должно быть не более двух копий запроса, потому что будет установлен флаг уничтожения объекта запроса. Для достижения этого мы можем delcare соответствующие функции-члены как delete или же privateи предоставить два экземпляра заявки на строительство.

Далее следует простая реализация запроса:

#include <atomic>
#include <future>
#include <memory>

template <class T>
class Request {
public:
struct Detail {
std::atomic<bool> is_canceled_{false};
std::promise<T> promise_;
std::future<T> future_ = promise_.get_future();
};

static auto NewRequest() {
std::unique_ptr<Request> copy1{new Request()};
std::unique_ptr<Request> copy2{new Request(*copy1)};

return std::make_pair(std::move(copy1), std::move(copy2));
}

Request(Request &&) = delete;

~Request() {
detail_->is_canceled_.store(true);
}

Request &operator=(const Request &) = delete;
Request &operator=(Request &&) = delete;

// simple api
std::promise<T> &Promise(const WorkerType &) {
return detail_->promise_;
}
std::future<T> &Future(const BossType &) {
return detail_->future_;
}

// return value:
// true if available, false otherwise
bool CheckAvailable() {
return detail_->is_canceled_.load() == false;
}

private:
Request() : detail_(new Detail{}) {}
Request(const Request &) = default;

std::shared_ptr<Detail> detail_;
};

template <class T>
auto SendMessage() {
auto result = Request<T>::NewRequest();
// TODO : send result.second(the another copy) to the worker
return std::move(result.first);
}

Новый запрос связан с функцией factroy NewRequestвозвращаемое значение std::pair который содержит два std::unique_ptrкаждый хранит копию вновь созданного запроса.

Теперь работник может использовать функцию-член CheckAvailable() проверить, отменен ли запрос.

И совместно используемое состояние управляется точно (я полагаю) std :: shared_ptr.

Обратите внимание на std::promise<T> &Promise(const WorkerType &): Ссылочный параметр const (который должен быть заменен типом propre в соответствии с вашей реализацией) предназначен для предотвращения случайного вызова боссом этой функции, в то время как рабочий должен иметь возможность легко предоставить аргумент propre для вызова этой функции. То же самое для std::future<T> &Future(const BossType &),

2

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]