我在一個io_service對象上使用了多個boost::asio::deadline_timer
。 std::shared_ptr
的boost::asio::deadline_timer
存儲在帶有索引的容器std::map<int, std::shared_ptr<debug_tim>> timers
中。如何避免射擊已經破壞了boost :: asio :: deadline_timer
在定時器處理程序中,我擦除其他boost::asio::deadline_timer
。但是,似乎擦除計時器通常會被成功錯誤代碼解僱。
有什麼辦法可以避免這種情況。我預計與刪除的boost::asio::deadline_timer
相對應的定時器處理程序始終會以Operation canceled
觸發。
我錯過了什麼嗎?
這裏是再現行爲
https://wandbox.org/permlink/G0qzYcqauxdqw4i7
#include <iostream>
#include <memory>
#include <boost/asio.hpp>
// deadline_timer with index ctor/dtor print
struct debug_tim : boost::asio::deadline_timer {
debug_tim(boost::asio::io_service& ios, int i) : boost::asio::deadline_timer(ios), i(i) {
std::cout << "debug_tim() " << i << std::endl;
}
~debug_tim() {
std::cout << "~debug_tim() " << i << std::endl;
}
int i;
};
int main() {
boost::asio::io_service ios;
std::map<int, std::shared_ptr<debug_tim>> timers;
{
for (int i = 0; i != 5; ++i) {
auto tim = std::make_shared<debug_tim>(ios, i);
std::cout << "set timer " << i << std::endl;
tim->expires_from_now(boost::posix_time::seconds(1));
timers.emplace(i, tim);
tim->async_wait([&timers, i](auto ec){
std::cout << "timer fired " << i << " : " << ec.message() << std::endl;
auto it = timers.find(i);
if (it == timers.end()) {
std::cout << " already destructed." << std::endl;
}
else {
int other_idx = i + 1; // erase other timer (e.g. i + 1)
timers.erase(other_idx);
std::cout << " erased " << other_idx << std::endl;
}
}
);
}
}
ios.run();
}
我也呼籲boost::asio::deadline_timer::cancel()
之前,我抹去定時器的代碼。但是,我得到了類似的結果。這裏是取消版本:
https://wandbox.org/permlink/uM0yMFufkyn9ipdG
#include <iostream>
#include <memory>
#include <boost/asio.hpp>
// deadline_timer with index ctor/dtor print
struct debug_tim : boost::asio::deadline_timer {
debug_tim(boost::asio::io_service& ios, int i) : boost::asio::deadline_timer(ios), i(i) {
std::cout << "debug_tim() " << i << std::endl;
}
~debug_tim() {
std::cout << "~debug_tim() " << i << std::endl;
}
int i;
};
int main() {
boost::asio::io_service ios;
std::map<int, std::shared_ptr<debug_tim>> timers;
{
for (int i = 0; i != 5; ++i) {
auto tim = std::make_shared<debug_tim>(ios, i);
std::cout << "set timer " << i << std::endl;
tim->expires_from_now(boost::posix_time::seconds(1));
timers.emplace(i, tim);
tim->async_wait([&timers, i](auto ec){
std::cout << "timer fired " << i << " : " << ec.message() << std::endl;
auto it = timers.find(i);
if (it == timers.end()) {
std::cout << " already destructed." << std::endl;
}
else {
int other_idx = i + 1; // erase other timer (e.g. i + 1)
auto other_it = timers.find(other_idx);
if (other_it != timers.end()) {
other_it->second->cancel();
timers.erase(other_it);
}
std::cout << " erased " << other_idx << std::endl;
}
}
);
}
}
ios.run();
}
編輯
菲利克斯,謝謝你的回答。我瞭解boost::asio::deadline::timer::cancel()
的行爲。我總是需要關心boost::asio::deadline::timer
的使用期限。我是我的項目的實際代碼,`boost :: asio :: deadline :: timer`是另一個對象(如會話對象)的成員變量。在定時器處理程序中,它訪問該對象。這很危險。
我考慮如何編寫安全代碼。我想出了使用std::weak_ptr
爲了檢查對象的生命週期。
下面是更新後的代碼:
#include <iostream>
#include <memory>
#include <boost/asio.hpp>
// deadline_timer with index ctor/dtor print
struct debug_tim : boost::asio::deadline_timer {
debug_tim(boost::asio::io_service& ios, int i) : boost::asio::deadline_timer(ios), i(i) {
std::cout << "debug_tim() " << i << std::endl;
}
~debug_tim() {
std::cout << "~debug_tim() " << i << std::endl;
}
int i;
};
int main() {
boost::asio::io_service ios;
std::map<int, std::shared_ptr<debug_tim>> timers;
{
for (int i = 0; i != 5; ++i) {
auto tim = std::make_shared<debug_tim>(ios, i);
std::cout << "set timer " << i << std::endl;
tim->expires_from_now(boost::posix_time::seconds(1));
timers.emplace(i, tim);
// Capture tim as the weak_ptr wp
tim->async_wait([&timers, i, wp = std::weak_ptr<debug_tim>(tim)](auto ec){
std::cout << "timer fired " << i << " : " << ec.message() << std::endl;
// Check the lifetime of wp
if (!wp.lock()) std::cout << " timer freed." << std::endl; // return here on actual code
auto it = timers.find(i);
if (it == timers.end()) {
std::cout << " already destructed." << std::endl;
}
else {
int other_idx = i + 1; // erase other timer (e.g. i + 1)
timers.erase(other_idx);
std::cout << " erased " << other_idx << std::endl;
}
}
);
}
}
ios.run();
}
這是爲了避免訪問已刪除對象具有boost::asio::deadline_timer
的好辦法?
編輯
我weak_ptr的解決方案效果很好。
見 How to avoid firing already destroyed boost::asio::deadline_timer
謝謝你的回答。我瞭解dead_time_timer的行爲。我想避免或想要知道取消狀態的原因是我訪問超時處理程序中具有deadline_timer的對象。 現在,我的目標已經改變爲如何避免訪問定時器處理程序中的已刪除資源。所以我更新(添加)我的問題。你能檢查我的方法嗎? –
@ TakakoshiKondo是的,這很好。在同時發射時注意比賽條件。 – felix
感謝您的評論! –