RxCpp系列:(二)定时器

  在项目中我们经常要用到定时器,RxCpp的interval可以方便地用来构造定时任务。

int main(int argc, char const *argv[])
{
    std::cout << "main thread id:" << std::this_thread::get_id() << std::endl;
    
    observable<long> timer_ob = 
        observable<>::interval(std::chrono::seconds(1));
    
    timer_ob.subscribe( 
        [=](long counter) {
            std::cout << "do something... " << counter << ", thread id " << std::this_thread::get_id() << std::endl;
        },
        []() { 
            std::cout << "OnComplete... " << std::endl; 
        });
        
    return 0;
}
View Code

  这个定时器运行在了主线程,程序是不能够正常退出的。

 

  我们做些改动,让它运行在新线程上:

int main(int argc, char const *argv[])
{
    std::cout << "main thread id:" << std::this_thread::get_id() << std::endl;
    
    observable<long> timer_ob = 
        observable<>::interval(std::chrono::seconds(1)).subscribe_on(serialize_new_thread());
    
    timer_ob.subscribe( 
        [=](long counter) {
            std::cout << "do something... " << counter << ", thread id " << std::this_thread::get_id() << std::endl;
        },
        []() { 
            std::cout << "OnComplete... " << std::endl; 
        });

    // To block main thread.
    observable<>::create<int>(
        [](subscriber<int> dest){
            for (;;) {
                int key = std::cin.get();
                if ('q' == key) {
                    dest.on_completed();
                    break;
                }
                dest.on_next(key);
            }
        }).subscribe([](int key){
            std::cout << "User types key: " << (char)key << std::endl;
        },
        [](){
            std::cout << "User entered 'q' to exit." << std::endl;
        });  
        
    return 0;
}
View Code

  subscribe_on即指定被观察者的调度器,亦即运行的线程。同样的,可以用observe_on来指定观察者运行的线程,如不指定,则是与被观察者运行在同一线程,即调用是同步的。

int main(int argc, char const *argv[])
{
    std::cout << "main thread id:" << std::this_thread::get_id() << std::endl;
    
    observable<long> timer_ob = 
        observable<>::interval(std::chrono::seconds(1)).subscribe_on(serialize_new_thread()).
            map([](long counter) {
                    std::cout << "map " << counter << ", thread id " << std::this_thread::get_id() << std::endl;
                    return 2 * counter;
                });
    
    timer_ob.observe_on(observe_on_new_thread()).subscribe( 
        [=](long counter) {
            std::cout << "do something... " << counter << ", thread id " << std::this_thread::get_id() << std::endl;
        },
        []() { 
            std::cout << "OnComplete... " << std::endl; 
        });
    
    // To block main thread.
    observable<>::create<int>(
        [](subscriber<int> dest){
            for (;;) {
                int key = std::cin.get();
                if ('q' == key) {
                    dest.on_completed();
                    break;
                }
                dest.on_next(key);
            }
        }).subscribe([](int key){
            std::cout << "User types key: " << (char)key << std::endl;
        },
        [](){
            std::cout << "User entered 'q' to exit." << std::endl;
        });  
        
    return 0;
}
View Code

运行结果:

main thread id:139853946795840
map 1, thread id 139853920990976
do something... 2, thread id 139853929383680
map 2, thread id 139853920990976
do something... 4, thread id 139853929383680
map 3, thread id 139853920990976
do something... 6, thread id 139853929383680
map 4, thread id 139853920990976
do something... 8, thread id 139853929383680
map 5, thread id 139853920990976
do something... 10, thread id 139853929383680
q
User entered 'q' to exit.

   map操作符可以用来转换数据流,比如这里是x2,它也可以改变数据流的类型。

 

多个观察者

  需要多个定时任务时,我们就需要建多个观察者了

    std::this_thread::sleep_for(std::chrono::milliseconds(2500));
    timer_ob.observe_on(observe_on_new_thread()).subscribe( 
        [=](long counter) {
            std::cout << "do something2... " << counter << ", thread id " << std::this_thread::get_id() << std::endl << std::flush;
        },
        []() { 
            std::cout << "OnComplete2... " << std::endl << std::flush; 
        });
View Code

运行结果:

main thread id:139668315871040
map 1, thread id 139668290066176
do something... 2, thread id 139668298458880
map 2, thread id 139668290066176
do something... 4, thread id 139668298458880
map 3, thread id 139668290066176
do something... 6, thread id 139668298458880
map 1, thread id 139668273280768
map 2, thread id 139668273280768
do something2... 2, thread id 139668281673472
do something2... 4map , thread id 3, thread id 139668281673472139668273280768

do something2... 6, thread id 139668281673472
map 4, thread id 139668273280768
map 4, thread id 139668290066176
do something2... 8, thread id 139668281673472
do something... 8, thread id 139668298458880
map 5, thread id 139668273280768
map 5, thread id 139668290066176
do something2... 10, thread id 139668281673472
do something... 10, thread id 139668298458880
map 6, thread id 139668273280768
map 6, thread id 139668290066176
do something2... 12, thread id 139668281673472
do something... 12, thread id 139668298458880
q
User entered 'q' to exit.

  我们明明只建了一个被观察者,为什么加一个观察者之后,被观察者看起来也多了一个?

  在ReactiveX中,被观察者有冷热之分:

    冷的可观察对象,可以理解为是在被订阅时创建,观察者无论先后,都能看到完整的数据流。比如上面的,尽管第二个观察者延迟2.5s才订阅,但它接收到的观测序列也是从1开始的;

    热的可观察对象,可以理解为被观察者创建之后,就开始在发送数据流,观察者订阅时只能从最新的开始拿起。这类似直播,无论什么时候开始看,都只能看到最新的画面。

 

  而要把冷的可观察对象转为热的可观察对象,publish即可:

    connectable_observable<long> timer_ob = 
        observable<>::interval(std::chrono::seconds(1)).subscribe_on(serialize_new_thread()).
            map([](long counter) {
                    std::cout << "map " << counter << ", thread id " << std::this_thread::get_id() << std::endl << std::flush;
                    return 2 * counter;
                }).publish();
                
    timer_ob.connect();

  注意,此时构建的可观察对象已经是connectable_observable了,要用connect来触发产生可观测序列。

 

  好了,此时看起来没啥大问题了,我们可以用一个定时器,来触发两个定时任务。

  等等,如果定时任务周期不一样咋办?比如第二个任务要5s触发一次?

  好办,还记得前面用过的filter操作符吧,我们可以用它来做这个事情。但,现在我想用一个新的操作符buffer来做同样的事情。

    timer_ob.observe_on(observe_on_new_thread()).buffer(1, 5).subscribe( 
        [=](std::vector<long> counter) {
            std::cout << "do something2... " << counter[0] << ", thread id " << std::this_thread::get_id() << std::endl << std::flush;
        },
        []() { 
            std::cout << "OnComplete2... " << std::endl << std::flush; 
        });

  buffer操作,是把序列缓存起来,再一块发送给后级,所以它给后级的是一组数据。上面的buffer(1, 5),是输出1个,跳过4个的意思。这样后级的周期就延长了。

 

  嗯,我觉得这两个任务资源消耗都挺少的,各自占一个线程觉得有点浪费啊,那我们也改一下:

int main(int argc, char const *argv[])
{
    std::cout << "main thread id:" << std::this_thread::get_id() << std::endl;
    
    observe_on_one_worker o1 = observe_on_event_loop();
    
    connectable_observable<long> timer_ob = 
        observable<>::interval(std::chrono::seconds(1)).subscribe_on(serialize_new_thread()).observe_on(o1).publish();
                
    timer_ob.connect();
    
    timer_ob.subscribe( 
        [=](long counter) {
            std::cout << "do something... " << counter << ", thread id " << std::this_thread::get_id() << std::endl << std::flush;
        },
        []() { 
            std::cout << "OnComplete... " << std::endl << std::flush; 
        });
        
    std::this_thread::sleep_for(std::chrono::milliseconds(2500));
    timer_ob.buffer(1, 5).subscribe( 
        [=](std::vector<long> counter) {
            std::cout << "do something2... " << counter.at(0) <<  ", thread id " << std::this_thread::get_id() << std::endl << std::flush;
        },
        []() { 
            std::cout << "OnComplete2... " << std::endl << std::flush; 
        });
    
    // To block main thread.
    observable<>::create<int>(
        [](subscriber<int> dest){
            for (;;) {
                int key = std::cin.get();
                if ('q' == key) {
                    dest.on_completed();
                    break;
                }
                dest.on_next(key);
            }
        }).subscribe([](int key){
            std::cout << "User types key: " << (char)key << std::endl;
        },
        [](){
            std::cout << "User entered 'q' to exit." << std::endl;
        });  
        
    return 0;
}
View Code

运行结果:

main thread id:139763986118464
do something... 1, thread id 139763960313600
do something... 2, thread id 139763960313600
do something... 3, thread id 139763960313600
do something... 4, thread id 139763960313600
do something2... 4, thread id 139763960313600
do something... 5, thread id 139763960313600
do something... 6, thread id 139763960313600
do something... 7, thread id 139763960313600
do something... 8, thread id 139763960313600
do something... 9, thread id 139763960313600
do something2... 9, thread id 139763960313600
do something... 10, thread id 139763960313600
do something... 11, thread id 139763960313600
q
User entered 'q' to exit.

  搞定,两个定时任务运行在同一线程了。

  

 

posted @ 2021-11-17 21:33  wolf_cgl  阅读(536)  评论(0)    收藏  举报