Nana:防止耗時處理導致介面的阻塞

來源:互聯網
上載者:User

絕大多數事件回呼函數都會很快地執行完成,並不會造成對介面的假死。Nana庫的事件模型是對事件隊列的順序處理,這意味著當前一個事件處理函數完成之後才會調用下一個。

考慮下面的例子:

#include <nana/gui/wvl.hpp>#include <nana/gui/widgets/button.hpp>#include <nana/gui/widgets/progress.hpp>class example    : public nana::gui::form{public:    example()    {        btn_start_.create(*this, nana::rectangle(10, 10, 100, 20));        btn_start_.caption(STR("Start"));        btn_start_.make_event<nana::gui::events::click>(*this, &example::_m_start);        btn_cancel_.create(*this, nana::rectangle(120, 10, 100, 20));        btn_cancel_.caption(STR("Cancel"));        btn_cancel_.make_event<nana::gui::events::click>(*this, &example::_m_cancel);        prog_.create(*this, nana::rectangle(10, 40, 280, 20));    }private:    void _m_start()    {        working_ = true;        btn_start_.enabled(false);        prog_.amount(100);        for(int i = 0; i < 100 && working_; ++i)        {            nana::system::sleep(1000); //a long-running simulation            prog_.value(i + 1);        }        btn_start_.enabled(true);    }    void _m_cancel()    {        working_ = false;    }private:    bool working_;    nana::gui::button btn_start_;    nana::gui::button btn_cancel_;    nana::gui::progress prog_;};int main(){    example ex;    ex.show();    nana::gui::exec();    return 0;} 

這個簡單的程式類比了一個耗時的操作,一個Start按鈕和一個Cancel按鈕分別表示啟動和中止任務。不難想象,_m_start()會執行一個耗時的操作,當按下"Start"按鈕之後,會導致介面的假死。這時單擊“Cancel”也是沒有用的,因為對Start按鈕的事件處理還沒有結束,能做的只有等待。可以看到,即使釋放了滑鼠,按鈕還是按下的狀態,因為單擊事件還在處理中。  

通常,解決這類耗時問題就是將耗時的處理過程放置到一個單獨的線程中,讓UI線程有閒置時間來響應使用者的操作,當耗時的操作完成,它就將結果返回給UI線程顯示。

考慮下面的方案:

#include <nana/gui/wvl.hpp>#include <nana/gui/widgets/button.hpp>#include <nana/gui/widgets/progress.hpp>#include <nana/threads/pool.hpp>class example    : public nana::gui::form{public:    example()    {        btn_start_.create(*this, nana::rectangle(10, 10, 100, 20));        btn_start_.caption(STR("Start"));        btn_start_.make_event<nana::gui::events::click>(nana::threads::pool_push(pool_, *this, &example::_m_start));        btn_cancel_.create(*this, nana::rectangle(120, 10, 100, 20));        btn_cancel_.caption(STR("Cancel"));        btn_cancel_.make_event<nana::gui::events::click>(*this, &example::_m_cancel);        prog_.create(*this, nana::rectnagle(10, 40, 280, 20));        this->make_event<nana::gui::events::unload>(*this, &example::_m_cancel);    }private:    void _m_start()    {        working_ = true;        btn_start_.enabled(false);        prog_.amount(100);        for(int i = 0; i < 100 && working_; ++i)        {            nana::system::sleep(1000); //a long-running simulation            prog_.value(i + 1);        }        btn_start_.enabled(true);    }    void _m_cancel()    {        working_ = false;    }private:    volatile bool working_;    nana::gui::button btn_start_;    nana::gui::button btn_cancel_;    nana::gui::progress prog_;    nana::threads::pool pool_;};int main(){    example ex;    ex.show();    nana::gui::exec();    return 0;} 

Nana庫提供了一個線程池的類。解決這類問題時,使用線程池可以擺脫對線程的管理,例如,建立,等待和銷毀的問題。上面兩段代碼非常的相似,但是最主要的區別是_m_start() 被指派到線程池中並有線程池中,並有池中的線程處理, UI線程也因此不會被阻塞並有閒置時間來響應使用者操作。

這裡有一個叫pool_push()的函數,它建立一個pool_pusher函數對象用於把_m_start()函數推入線程池,換句話說,就是將pool_pusher函數對象當作了事件處理,當點擊Start按鈕時,pool_pusher會被調用,同時_m_start()則被推入到線程池中執行。 

在該版本中,form對象註冊了一個unload事件並調用_m_cancel(),當關閉視窗的時候,程式放棄了剩餘的耗時操作。但是這裡有一個問題需要回答,當耗時操作還在工作的時候,關閉視窗會導致按鈕和進度條也同樣被銷毀,但是耗時操作並為結束,如果此時耗時操作訪問了按鈕和進度條對象是否會導致程式崩潰?回答是會崩潰,但是上面的代碼會避免在耗時操作完成之前銷毀按鈕和進度條,在類中,線程池的對象聲明在按鈕和進度條之後,這意味著線程池會在按鈕和進度條之前析構,當析構線程池的時候,它會等待所有的背景工作執行緒都已經結束。

在後台線程中處理阻塞操作  

在某些情況下,耗時工作不能被取消,也不知道當前的進度。程式通常會用一直滾動的進度條表示正在處理。

#include <nana/gui/wvl.hpp>#include <nana/gui/widgets/button.hpp>#include <nana/gui/widgets/progress.hpp>#include <nana/threads/pool.hpp>class example    : public nana::gui::form{public:    example()    {        using namespace nana::gui;        btn_start_.create(*this, nana::rectnagle(10, 10, 100, 20));        btn_start_.caption(STR("Start"));        btn_start_.make_event<events::click>(nana::threads::pool_push(pool_, *this, &example::_m_start));        btn_start_.make_event<events::click>(nana::threads::pool_push(pool_, *this, &example::_m_ui_update));        prog_.create(*this, nana::rectangle(10, 40, 280, 20));        prog_.style(false);        this->make_event<events::unload>(*this, &example::_m_cancel);    }private:    void _m_start()    {        btn_start_.enabled(false);        nana::system::sleep(10000); //a blocking simulation        btn_start_.enabled(true);    }    void _m_ui_update()    {        while(btn_start_.enabled() == false)        {            prog_.inc();            nana::system::sleep(100);        }    }    void _m_cancel(const nana::gui::eventinfo& ei)    {        if(false == btn_start_.enabled())            ei.unload.cancel = true;    }private:    nana::gui::button btn_start_;    nana::gui::progress prog_;    nana::threads::pool pool_;};int main(){    example ex;    ex.show();    nana::gui::exec();    return 0;} 

單擊Start按鈕,程式會把_m_start()和_m_ui_update()都推入到線程池。是不是很容易?
歡迎各位提出意見,建議,並展開討論。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.