如何使用与 io_context 一起使用的不同 TimerInterval 类在类中调用具有自己的采样间隔的不同方法

问题描述 投票:0回答:1

我正在尝试使用与我在这篇文章中的问题的答案所建议的相同想法。这个目标是实现一个类,该类将在 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

有什么建议如何解决这个错误吗?

c++ boost intervals
1个回答
0
投票

您可以做任何事情来动态构造

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。

奖金

始终对对象和数据进行分组。索引 0,1,2 的重复切换表明某物想要成为一个实体:

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

© www.soinside.com 2019 - 2024. All rights reserved.