对于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会卡死的,启动和结束线程接口都是在主界面线程调用的哦!

祝大家生活愉快,欢迎探讨,互相学习进步~

 

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