我正在尝试使用与我在这篇文章中的问题的答案所建议的相同想法。这个目标是实现一个类,该类将在 start_trigger 方法内调用 3 个不同的方法,并使用它们自己的时间间隔。
以下示例代码编译成功,但在逻辑上不工作,因为 IntervalTimer 对象的生命周期仅限于 start_process() 方法而不是类生命周期。
所以我解决这个问题的解决方案是定义IntervalTimer的3个变量作为类成员,然后在start_processing方法中初始化它们。
start_process方法是一个公共方法,可以通过MyTestClass的对象来调用。
这是编译时没有问题的代码,但它不会重复调用三个方法(按照提供的间隔调用方法1、方法2和方法3)。
#include <boost/asio.hpp>
#include <iomanip>
#include <iostream>
#include <map>
#include <unordered_set>
#include <vector>
namespace asio = boost::asio;
using namespace std::chrono_literals;
using duration = std::chrono::steady_clock::duration;
static constexpr auto now = std::chrono::steady_clock::now;
using boost::system::error_code;
using namespace std;
enum MethodTypes { FIRST = 0, SECOND = 1, THIRD = 2 };
using Callback = std::function<void()>;
//template<typename Callback>
struct IntervalTimer {
IntervalTimer(asio::any_io_executor ex, duration interval, Callback callback)
: interval_(interval)
, cb_(std::move(callback))
, tim_(ex, interval_)
{
loop();
}
private:
void loop() {
while (tim_.expiry() <= now())
tim_.expires_at(tim_.expiry() + interval_);
tim_.async_wait([this](boost::system::error_code const& ec) {
if (!ec) { // aborted or other failure
cb_();
loop();
}
});
}
duration interval_;
Callback cb_;
asio::steady_timer tim_;
};
class MyTestClass final {
public:
MyTestClass(int transaction_id , boost::asio::io_context& io) :
transaction_id_(transaction_id) ,io_context_(io) {
//tt1(clone( IntervalTimer()));
}
void start_process (vector<int>ids, vector<bool> conditions, map <int, unordered_set<string> >& set_of_items);
void stop_process();
private:
void method1 (int id, const unordered_set<string>& set_of_buyers);
void method2 (int id, const unordered_set<string>& set_of_sellers);
void method3 (int id, const unordered_set<string>& set_of_units);
void stats_cb(uint32_t tp_id, MethodTypes method_type, unordered_set<string>& set_of_values);
int transaction_id_;
boost::asio::io_context& io_context_;
//IntervalTimer tt1;
//IntervalTimer tt2;
//IntervalTimer tt3;
};
void MyTestClass::start_process(vector<int>ids, vector<bool> conditions, map <int, unordered_set<string> >& set_of_items){
if (conditions[0]) {
auto sample_interval = 10;
//IntervalTimer tt1_tmp (io_context_.get_executor(), std::chrono::milliseconds(sample_interval), [&] {
// stats_cb(ids[0], MethodTypes::FIRST, set_of_items[0]);
//});
//tt1 =std::move(tt1_tmp);
IntervalTimer t1(io_context_.get_executor(), std::chrono::milliseconds(sample_interval), [&] {
stats_cb(ids[0], MethodTypes::FIRST, set_of_items[0]);
});
}
if (conditions[1]) {
auto sample_interval = 20;
IntervalTimer t2(io_context_.get_executor(), std::chrono::milliseconds(sample_interval), [&] {
stats_cb(ids[1], MethodTypes::SECOND, set_of_items[1]);
});
}
if (conditions[1]) {
auto sample_interval = 30;
IntervalTimer t3(io_context_.get_executor(), std::chrono::milliseconds(sample_interval), [&] {
stats_cb(ids[2], MethodTypes::THIRD, set_of_items[3]);
});
}
io_context_.run();
}
void MyTestClass::stats_cb(uint32_t id, MethodTypes method_type, unordered_set<string>& set_of_values){
switch (method_type) {
case MethodTypes::FIRST:
method1(id, set_of_values);
break;
case MethodTypes::SECOND:
method2(id, set_of_values);
break;
case MethodTypes::THIRD:
method3(id, set_of_values);
break;
default:
cout<<"the provided type is not supported"<< endl;
return;
}
}
void MyTestClass::method1 (int v, const unordered_set<string>& set_of_values){
cout<<"method-1 is called and v ="<<v<<endl;
for (auto e: set_of_values)
cout<<e<< endl;
cout<<"method-1 is done"<<endl;
}
void MyTestClass::method2 (int v, const unordered_set<string>& set_of_values){
cout<<"method-2 is called and v ="<<v<<endl;
for (auto e: set_of_values)
cout<<e<< endl;
cout<<"method-2 is done"<<endl;
}
void MyTestClass::method3 (int v, const unordered_set<string>& set_of_values){
cout<<"method-3 is called and v ="<<v<<endl;
for (auto e: set_of_values)
cout<<e<< endl;
cout<<"method-3 is done"<<endl;
}
int main() {
boost::asio::io_context iocx;
MyTestClass obj(100, iocx);
vector<int> ids {100,200,300};
vector<bool> conds {false,true,true};
map <int, unordered_set<string> > mp;
mp[0].insert("test1_0");
mp[0].insert("test1_1");
mp[0].insert("test1_2"); mp[1].insert("test2_0");
mp[1].insert("test2_1");
mp[1].insert("test2_2"); mp[2].insert("test3_0");
mp[2].insert("test3_1");
obj.start_process(ids, conds, mp);
return 0;
}
但是,当我取消注释注释部分时,例如 MyTestClass 中的以下定义
//IntervalTimer tt1;
以及 start_process() 中的以下一个
//IntervalTimer tt1_tmp (io_context_.get_executor(), std::chrono::milliseconds(sample_interval), [&] {
// stats_cb(ids[0], MethodTypes::FIRST, set_of_items[0]);
//});
//tt1 =std::move(tt1_tmp);
我收到以下编译错误:
prog.cc: In constructor 'MyTestClass::MyTestClass(int, boost::asio::io_context&)':
prog.cc:53:56: error: no matching function for call to 'IntervalTimer::IntervalTimer()'
53 | transaction_id_(transaction_id) ,io_context_(io) {
| ^
prog.cc:23:5: note: candidate: 'IntervalTimer::IntervalTimer(boost::asio::any_io_executor, duration, Callback)'
23 | IntervalTimer(asio::any_io_executor ex, duration interval, Callback callback)
| ^~~~~~~~~~~~~
prog.cc:23:5: note: candidate expects 3 arguments, 0 provided
prog.cc:22:8: note: candidate: 'IntervalTimer::IntervalTimer(IntervalTimer&&)'
22 | struct IntervalTimer {
| ^~~~~~~~~~~~~
prog.cc:22:8: note: candidate expects 1 argument, 0 provided
有什么建议如何解决这个错误吗?
您可以做任何事情来动态构造
IntervalTimer
对象。例如。有一个喜欢的会员
std::list<IntervalTimer> timers;
然后,还修复了一些复制粘贴错误(重复
conditions[1]
并且set_of_items[3]
始终是空集...):
void MyTestClass::start_process(std::vector<int> ids, std::vector<bool> conditions,
std::map<int, Set>& set_of_items) {
auto ex = io_context_.get_executor();
if (conditions[0])
timers.emplace_back(ex, 10ms, [&] { stats_cb(ids[0], MethodTypes::FIRST, set_of_items[0]); });
if (conditions[1])
timers.emplace_back(ex, 20ms, [&] { stats_cb(ids[1], MethodTypes::SECOND, set_of_items[1]); });
if (conditions[2])
timers.emplace_back(ex, 30ms, [&] { stats_cb(ids[2], MethodTypes::THIRD, set_of_items[2]); });
}
有一个大问题,你通过引用捕获本地
ids
。这会导致未定义的行为。见下文。
一般来说,通过减少重复来简化代码。有很多复制/粘贴错误。另外,永远不要使用命名空间 std。 始终为类型和对象使用好的名称。
当您不断在索引 (0,1,2) 上切换相同的代码时,请考虑将行为和数据分组到一个类中。不要添加无用的重复/间接,如
stats_cb
,而只需写:
void MyTestClass::start_process(std::vector<int> ids, std::vector<bool> conditions,
std::map<int, Items>& set_of_items) {
if (conditions[0])
timers.emplace_back(ex_, 10ms, [this, id = ids[0], &items = set_of_items[0]] { method1(id, items); });
if (conditions[1])
timers.emplace_back(ex_, 20ms, [this, id = ids[1], &items = set_of_items[0]] { method2(id, items); });
if (conditions[2])
timers.emplace_back(ex_, 30ms, [this, id = ids[2], &items = set_of_items[0]] { method3(id, items); });
}
请勿在
run
内打电话给
start_process
。 MyTestClass 不拥有 io_context (并且名称 start_process
是错误的,因为它也完成了该过程)。投入stop_process
:
void MyTestClass::stop_process() {
for (auto& t : timers)
t.cancel();
}
我们得到
住在Coliru
#include <boost/asio.hpp>
#include <iomanip>
#include <iostream>
#include <list>
#include <map>
#include <unordered_set>
#include <vector>
namespace asio = boost::asio;
using namespace std::chrono_literals;
using duration = std::chrono::steady_clock::duration;
static constexpr auto now = std::chrono::steady_clock::now;
using boost::system::error_code;
static auto const start = now();
static inline void trace(auto const&... msg) {
((std::cout << (now() - start) / 1ms << "\tms\t") << ... << msg) << std::endl;
}
struct IntervalTimer {
using Callback = std::function<void()>;
IntervalTimer(asio::any_io_executor ex, duration interval, Callback callback)
: interval_(interval)
, cb_(std::move(callback))
, tim_(ex, interval_) {
loop();
}
void cancel() {
post(tim_.get_executor(), [this] { tim_.cancel(); });
}
private:
void loop() {
while (tim_.expiry() <= now())
tim_.expires_at(tim_.expiry() + interval_);
tim_.async_wait([this](error_code const& ec) {
if (!ec) { // aborted or other failure
cb_();
loop();
}
});
}
duration interval_;
Callback cb_;
asio::steady_timer tim_;
};
using Items = std::unordered_set<std::string>;
class MyTestClass final {
public:
MyTestClass(int transaction_id, asio::any_io_executor ex) : transaction_id_(transaction_id), ex_(ex) {}
void start_process(std::vector<int> ids, std::vector<bool> conditions,
std::map<int, std::unordered_set<std::string>>& set_of_items);
void stop_process();
private:
void method1(int id, Items const& set_of_buyers);
void method2(int id, Items const& set_of_sellers);
void method3(int id, Items const& set_of_units);
int transaction_id_; // UNUSED
asio::any_io_executor ex_;
std::list<IntervalTimer> timers;
};
void MyTestClass::start_process(std::vector<int> ids, std::vector<bool> conditions,
std::map<int, Items>& set_of_items) {
// for (int id = 0; id < 3; ++id)
// timers.emplace_back(ex_, 10ms, [id, &items = set_of_items[id]] { method1(id, items); });
if (conditions[0])
timers.emplace_back(ex_, 1000ms, [this, id = ids[0], &items = set_of_items[0]] { method1(id, items); });
if (conditions[1])
timers.emplace_back(ex_, 2000ms, [this, id = ids[1], &items = set_of_items[0]] { method2(id, items); });
if (conditions[2])
timers.emplace_back(ex_, 3000ms, [this, id = ids[2], &items = set_of_items[0]] { method3(id, items); });
}
void MyTestClass::stop_process() {
for (auto& t : timers)
t.cancel();
}
void MyTestClass::method1(int v, Items const& set_of_values) {
trace("method-1", " is called and v =" , v );
std::cout << "\t\tmethod-1" << " iterating:";
for (auto const& e : set_of_values)
std::cout << " " << e;
std::cout << std::endl;
trace("method-1", " is done");
}
void MyTestClass::method2(int v, Items const& set_of_values) {
trace("method-2", " is called and v =" , v );
std::cout << "\t\tmethod-2" << " iterating:";
for (auto const& e : set_of_values)
std::cout << " " << e;
std::cout << std::endl;
trace("method-2", " is done");
}
void MyTestClass::method3(int v, std::unordered_set<std::string> const& set_of_values) {
trace("method-3", " is called and v =" , v );
std::cout << "\t\tmethod-3" << " iterating:";
for (auto const& e : set_of_values)
std::cout << " " << e;
std::cout << std::endl;
trace("method-3", " is done");
}
int main() {
asio::io_context ioc;
MyTestClass obj(100, ioc.get_executor());
std::map<int, Items> mp = {{0, {"test1_0", "test1_1", "test1_2"}},
{1, {"test2_0", "test2_1", "test2_2"}},
{2, {"test3_0", "test3_1"}}};
obj.start_process({100, 200, 300}, {false, true, true}, mp);
ioc.run_for(8s);
obj.stop_process();
ioc.run(); // graceful shutdown
}
印刷
2000 ms method-2 is called and v =200
method-2 iterating: test1_2 test1_1 test1_0
2007 ms method-2 is done
3000 ms method-3 is called and v =300
method-3 iterating: test1_2 test1_1 test1_0
3000 ms method-3 is done
4000 ms method-2 is called and v =200
method-2 iterating: test1_2 test1_1 test1_0
4000 ms method-2 is done
6000 ms method-2 is called and v =200
method-2 iterating: test1_2 test1_1 test1_0
6000 ms method-2 is done
6000 ms method-3 is called and v =300
method-3 iterating: test1_2 test1_1 test1_0
6000 ms method-3 is done
8000 ms method-2 is called and v =200
method-2 iterating: test1_2 test1_1 test1_0
8000 ms method-2 is done
请注意,我将延迟时间拉得更长,以便您可以看到输出。您可以将它们调整回 10ms、20ms、30ms。
struct Task {
using Func = std::function<void(int, Items const&)>;
bool condition = false;
duration interval;
Func method;
};
如果你稍微眯起眼睛,你会发现
MyTestClass
此时实际上只会增加复杂性。它看起来像是一个承担了太多责任的类,具有讽刺意味的是,它阻止了需要存在的实体的存在。这个版本怎么样:
住在Coliru
#include <boost/asio.hpp>
#include <iomanip>
#include <iostream>
#include <list>
#include <map>
#include <unordered_set>
#include <vector>
namespace asio = boost::asio;
using namespace std::chrono_literals;
using duration = std::chrono::steady_clock::duration;
static constexpr auto now = std::chrono::steady_clock::now;
using boost::system::error_code;
static auto const start = now();
static inline void trace(auto const&... msg) {
((std::cout << (now() - start) / 1ms << "\tms\t") << ... << msg) << std::endl;
}
struct RecurringTask {
using Callback = std::function<void()>;
RecurringTask(asio::any_io_executor ex, duration interval, Callback callback)
: interval_(interval)
, cb_(std::move(callback))
, tim_(ex, interval_) {
loop();
}
void cancel() {
post(tim_.get_executor(), [this] { tim_.cancel(); });
}
private:
void loop() {
while (tim_.expiry() <= now())
tim_.expires_at(tim_.expiry() + interval_);
tim_.async_wait([this](error_code const& ec) {
if (!ec) { // aborted or other failure
cb_();
loop();
}
});
}
duration interval_;
Callback cb_;
asio::steady_timer tim_;
};
using Items = std::unordered_set<std::string>;
void method(std::string_view name, int v, Items const& set_of_values) {
trace(name, " is called and v = ", v);
std::cout << "\t\t" << name << " iterating:";
for (auto const& e : set_of_values)
std::cout << " " << e;
std::cout << std::endl;
trace(name, " is done");
}
int main() {
asio::io_context ioc;
int transaction_id_ = 100; // UNUSED
Items buyers = {"test1_0", "test1_1", "test1_2"};
Items sellers = {"test2_0", "test2_1", "test2_2"};
Items units = {"test3_0", "test3_1"};
std::list<RecurringTask> tasks;
// tasks.emplace_back(ioc.get_executor(), 1000ms, bind(method, "buyers", 100, std::ref(buyers)));
tasks.emplace_back(ioc.get_executor(), 2000ms, bind(method, "sellers", 200, std::ref(sellers)));
tasks.emplace_back(ioc.get_executor(), 3000ms, bind(method, "units", 300, std::ref(units)));
ioc.run_for(8s);
}
印刷
2000 ms sellers is called and v = 200
sellers iterating: test2_2 test2_1 test2_0
2016 ms sellers is done
3000 ms units is called and v = 300
units iterating: test3_1 test3_0
3000 ms units is done
4000 ms sellers is called and v = 200
sellers iterating: test2_2 test2_1 test2_0
4000 ms sellers is done
6000 ms sellers is called and v = 200
sellers iterating: test2_2 test2_1 test2_0
6000 ms sellers is done
6000 ms units is called and v = 300
units iterating: test3_1 test3_0
6000 ms units is done
8000 ms sellers is called and v = 200
sellers iterating: test2_2 test2_1 test2_0
8006 ms sellers is done