Я пытаюсь написать свою собственную торрент-программу на основе libtorrent rasterbar, и у меня возникают проблемы с корректной работой механизма оповещения. Libtorrent предлагает функцию
void set_alert_notify (boost::function<void()> const& fun);
который должен
Назначение функции состоит в том, что клиент пробуждает свой основной поток, чтобы опросить дополнительные оповещения с помощью pop_alerts (). Если функция уведомления не может это сделать, она больше не будет вызываться до тех пор, пока по какой-либо другой причине не будет вызван pop_alerts.
пока все хорошо, я думаю, что понимаю смысл этой функции. Тем не менее, моя фактическая реализация не работает так хорошо. Мой код до сих пор выглядит так:
std::unique_lock<std::mutex> ul(_alert_m);
session.set_alert_notify([&]() { _alert_cv.notify_one(); });
while (!_alert_loop_should_stop) {
if (!session.wait_for_alert(std::chrono::seconds(0))) {
_alert_cv.wait(ul);
}
std::vector<libtorrent::alert*> alerts;
session.pop_alerts(&alerts);
for (auto alert : alerts) {
LTi_ << alert->message();
}
}
однако есть условие гонки. Если wait_for_alert
возвращается NULL
(поскольку оповещений пока нет), но функция передана set_alert_notify
называется раньше _alert_cw.wait(ul);
, весь цикл ждет вечно (из-за второго предложения из цитаты).
На данный момент мое решение просто меняется _alert_cv.wait(ul);
в _alert_cv.wait_for(ul, std::chrono::milliseconds(250));
что уменьшает количество циклов в секунду, сохраняя достаточно низкую задержку.
Но это действительно более обходной путь, чем решение, и я продолжаю думать, что должен быть правильный способ справиться с этим.
Вам нужна переменная для записи уведомления. Он должен быть защищен тем же мьютексом, который владеет условной переменной.
bool _alert_pending;
session.set_alert_notify([&]() {
std::lock_guard<std::mutex> lg(_alert_m);
_alert_pending = true;
_alert_cv.notify_one();
});
std::unique_lock<std::mutex> ul(_alert_m);
while(!_alert_loop_should_stop) {
_alert_cv.wait(ul, [&]() {
return _alert_pending || _alert_loop_should_stop;
})
if(_alert_pending) {
_alert_pending = false;
ul.unlock();
session.pop_alerts(...);
...
ul.lock();
}
}
Других решений пока нет …