對於gtk多線程編程的一些思考以及實踐歸納系列(2)

上一篇提到了一個問題,如何在gtk界面保持友好的情況下(不會卡界面),執行來自於功能上的各種任務(耗時間)呢?在此,我們對於這個問題作出分析。

第一種任務:多個連續不斷的任務

其實,這裏面很關鍵的一個問題就是,我們執行的任務是什麼樣的任務!!記得上篇文章https://blog.csdn.net/FlayHigherGT/article/details/84932747我們提到的任務其實是一個連續不斷需要執行的任務,類似網絡任務,針對網絡任務,我需要重複不斷的去請求不同的網絡接口,因此我們給出下面的解決方案:

1、首先我們在界面開始的時候創建一個線程以及一個全局任務隊列(確保線程安全,做好鎖操作),線程啓動之後就時刻準備着接收網絡任務的需求,一旦隊列中任務請求,線程立馬進行操作(其實這裏面如果用多個線程來實現的話類似一個線程池,只是界面任務可能沒那麼頻繁,一個線程足以)。

2、在主界面需要執行任務的時候,將需要執行的參數以及執行完任務需要作出的處理函數指針包裹在隊列數據結構裏面,入隊。

3、線程得到隊列裏面的任務,進行任務處理,這個過程可能會延續好幾秒鐘,但是沒事這是在線程中完成的,完成請求之後回調那個傳進來的函數指針,即可在主界面完成相應的反饋。

4、回調函數肯定有刷新界面等操作,因此這個回調函數必須要在gdk_threads_add_idle中完成,不然在線程中操作ui系統必然奔潰(這一條有待商榷,不過能肯定的是,如果在副線程進行主線程ui的操作,那肯定需要用到gdk_threads_add_idle)。


當然,在網絡線程中我們需要實現請求網絡的功能,只需我們傳入不同的參數即可完成不同網絡請求的那種,這裏推薦c語言網絡請求庫:libcurl——詳情可見另一篇博客https://blog.csdn.net/FlayHigherGT/article/details/83583201

第二種方式:單一任務

第二種任務其實就是普通單一的耗時任務,比如說一個客戶端界面需要進行一個遠程rdp連接,在配置好參數之後,按下按鈕,就能進行連接,連接過程可能很耗時間,我們需要給個提示正在連接,一旦連不上,就去掉正在連接,給出錯誤信息,一旦連上了,也需要去掉正在連接的標籤。那麼這種我們應該怎麼去做呢?

void stop_rdp_thread(void)
{
    if (g_thrdp) {
        system("killall -9 xfreerdp");
        M_DEBUG("killall -9 xfreerdp");
        g_thread_join(g_thrdp);
        g_thrdp = NULL;
    }
}

static gboolean start_connect_rdp_task(gchar* ip)
{
    g_thrdp = g_thread_new(NULL,(GThreadFunc)connect_windows_system,(gpointer)ip);
    if (g_thrdp == NULL)
        return FALSE;
    return TRUE;
}

static gboolean connect_windows_system(gpointer data)
{
    gchar* guest_ip = (gchar*)data;
    res = system(rdp_connect_cmd);
    //system會阻塞住整個線程(不管xfreerdp是否執行成功),如果exit_logined_window裏面kill xfreerdp之後system會直接返回0
    if (res == 33536 || res == 8192 || res == 2) {
        //33536:虛擬機開啓了防火牆  8192:待測
        gdk_threads_add_idle((GSourceFunc)message_box, (gpointer)"虛擬機配置有誤,RDP連接失敗!");
    }

    connect_rdp_callback();//這邊不管連接成功不成功,都把正在連接的按鈕去掉
    return FALSE;
}

看上面這段代碼,點擊連接按鈕,觸發線程連接rdp,如果連接成功,線程中卡死等待用戶自己退出,如果連接失敗,線程中給出界面提示,同時不管連接成功不成功都去掉正在連接的標籤,這裏特別注意的是在線程中要作出界面相關的操作必須用        gdk_threads_add_idle((GSourceFunc)message_box, (gpointer)"虛擬機配置有誤,RDP連接失敗!");進行包裹。不然主界面會卡死。message是主線程中的一個信息提示界面。

最後,如果主界面在執行任務的過程中突然想取消,按下取消按鈕,執行stop_rdp_thread,那麼自然會銷燬線程。

最後總結一下:

在界面操作類似頻繁的任務是,可以實現一個統一的方法線程,在裏面做處理,用一個線程安全的全局隊列進行任務的接收,記住這個方法線程一定要有啓動的接口和結束的接口,啓動的接口不必多說,啓動線程,結束的接口需要你把線程中卡死的任務給kill掉,之後進行join,不然join會卡死的,啓動和結束線程接口都是在主界面線程調用的哦!

祝大家生活愉快,歡迎探討,互相學習進步~

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章