开发者学堂课程【物联网开发- Linux 高级程序设计全套视频:Gtk 线程】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/660/detail/11070
Gtk 线程
内容介绍
一、Gtk 线程
二、代码
一、Gtk 线程
1.Gtk 线程创建函数
(1)、Gtk 提供了一个接口函数,用来创建线程, Gtk 是一个库函数,gtk 当中创建线程调用 pthread_creat() 进行封装。
2、Gtk 线程创建多线程
(1)、一般 GUI 应用程序默认只执行一个线程,以前的应用程序只有一个 main 函数,只有一个任务,每次只执行一个操作,如果某个操作耗时较长,则用户界面会卡住,出现冻结的现象。所以若某个操作的时间比较长一般会创建线程去处理。
(2)、GTK+ 应用程序中创建多线程
除了通过 POSIX 线程函数 pthread_create() 创建线程外,实际编程时,还可以通过 GTK+ 线程函数 g_thread_create() 创建一个线程。
g_thread_create() 中调用了 pthread_create() ,因为只有操作系统提供的接口才能创建线程,因为创建线程是内核去创建的,需要通过系统调用去进入内核。
(3) 、GTK 的界面相关的代码不是线程安全函数。比如设置 lablel的内容,切换背景图片,不是线程安全函数。
3、gdk_threads_enter() 函数、 gdk_threads_leave()函数和线程初始化
(1)、新线程中执行图形绘制相关的代码时,需要在绘图之前 gdk_threads_enter() 函数和绘完图之后加gdk_threads_leave()函数,gdk_threads_enter() 和 gdk_threads_leave() 之间用尽量少的代码。在线程中执行代码 gdk_threads_enter() ,立马去设置 label 的内容,
设置完 label 的内容,之后立马执行 gdk_threads_leave() ,相当于互斥锁,使用完gdk_threads_enter() 之后别的线程不能绘制图,当执行完 gdk_threads_leave() 之后别的线程才能绘制图,以保证对 GTK 界面的操作是互斥的。
(2)、gtk 中使用多线程需进行相应初始化。将来在做播放器,播放器是一个多任务的,多个线程的,就必须进行线程初始化,在多个线程当中都去操作界面,必须进行线程初始化。
(3)、gtk 中使用多线程需进行相应初始化。那并且编译时需要链接 GTK 线程库-1gthread-2.0
二、代码讲解
1.程序执行效果
CC=gcc
//用 gcc 编译目标 demo.c
MAINC=demo.c
EXEC=demo
//make_file 的目标是 demo
CFLAGS=‘pkg-config --cflags --libs gtk+-2.0’
//编译选项
main:
$(cc) $(MAINC) -o $(EXEC) $(CFLAGS) -lgthread-2.0
//cc是gcc,$(MAINC) -o $(EXEC)是demo
Clean:
Rm %(EXEC) -rf
Make 之后生成一个 demo
运行 demo
生成效果图
这个界面一个窗口中有俩个 lable ,上边的变得慢,下边的变得快,上边的是0.5秒,下边的是1秒。
在主线程当中去创建俩个线程,一个线程操作 label1 ,另一个线程去操作label2 ,两个线程分别操作一个 label ,更改 label 的内容,一个线程是0.5秒改一次,一个线程是1秒改一次。
2.代码
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
//线程处理函数1
//data 的形参相当于创建线程时传递的 lablel
gpointer deal_thread(gpointer data)
{
int i=0;
//定义 I
while(1){
char buf[20] ="";
sprintf(buf,"%d",i++);
//在线程中每次循环用 i++,将 i 的值放入 buf 中
printf("i=====%d\n",i);
usleep(500*1000);
//每次循环需要0.5秒
//使用 qdk thread *() 将要刷新的界面
gdk_threads_enter():
// 更改界面之前调用 gdk_threads_enter() ,进入多线程互斥区域
gtk_label_set_text( GTK_LABEL(data),buf );
//更改标签的标识(更改图形界面的内容),将 buf 中 i 的值显示如data 中,data 是传入的标签的标识
gdk_threads_leave();
//更改界面之后调用 gdk_threads_leave() ,退出多线程互斥区域
//在线程之间加载界面必须调用 gdk_threads_enter()和gdk_threads_leave()
}
}
//线程处理函数2
//data 的形参相当于创建线程时传递的 lablel1
gpointer deal_thread1(gpointer data){
int k=10;
//定义 k
while(1){
char buf[20] ="";
sprintf(buf,"%d",k++);
//在线程中每次循环用 k++ ,将 i 的值放入 buf 中
printf("k=====%d\n",k);
sleep(1);
//每次循环需要1秒
//使用 qdk thread *() 将要刷新的界面
gdk_threads_enter():
// 更改界面之前调用 gdk_threads_enter() ,进入多线程互斥区域
gtk_label_set_text( GTK_LABEL(data),buf );
//更改标签的标识(更改图形界面的内容),将 buf 中 k 的值显示如data 中,data 是传入的标签的标识
gdk_threads_leave();
//更改界面之后调用 gdk_threads_leave()
,退出多线程互斥区域
//在线程之间加载界面必须调用gdk_threads_enter()和gdk_threads_leave()
,为实现界面上之间的互斥俩者之间时间尽量要短
}
}
void gtk_thread_init(void)
{
if(FALSE == g_thread_supported()) {
g_thread_init(NULL);
}
gdk_threads_init();
}
//在做播放器的时候将此函数调用,在main函数的开始调用此函数,线程初始化gtk_thread_init()
,如果用 pthread_create()
,也需要调用gtk_thread_init()
int main(int argc,char *argv[])
{
gtk_thread_init();
//在main函数中如果创建了多任务,并且在多任务当中操作界面,必须调用 gtk_thread_init()
而且放在 gtk_init()
之前,
Gtk_init(&argc,&argv);
//创建新窗口
GtkWidget *window = NULL;
GtkWidget *vbox = NULL;
//在多个线程当中操作图形界面 gtk_init() ,有 window , vbox垂直步局
Window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window),"thread number");
Gtk_widget_set_size_request(window,200,60);
g_signal_connect(window,"destroy",G_CALLBACK(gtk_main_q);
//创建窗口
vbox = gtk_vbox_new(TRUE,5);
gtk_container_add(GTK_CONTAINER(window),vbox);
//创建垂直布局,垂直布局放在窗口内
GtkWidget *label = gtk_label_new("");
//创建 lablel , lablel无内容
GtkWidget *label1=gtk label new("");
//创建 lablel1,lablel1 无内容
gtk_box_pack_start(GTK BOX(vbox),label,TRUE,TRUE,5);
//将 lablel 放入垂直布局里
gtk_box_pack_start(GTK BOX(vbox),label1TRUETRUE-5);
//将 lablel1 放入垂直布局里
//垂直布局内得到俩标签,对界面来说是窗口,窗口里有个垂直布局,垂直布局里有俩 lablel
/* 创建线程
GThread *q_thread_create(GThreadFunc func,
gpointer data,
gboolean joinable,
GError **error);
//这是创建线程函数,func 是线程执行的外部函数,data 是传给该
g_thread_create( (GThreadFunc)deal_thread, label, FALSE,NULL);
g_thread_create((GThreadFunc)deal_thread1,label1,FALSE,NULL);
//创建了俩个线程,或者调用 pthread_create 创建俩个线程。
//deal_thread 和 deal_thread1 是回调函数,必须强制类型转换
// label 和 label1 是给线程回调函数传参数
//FALSE 可使 g_thread_create 创建的线程为不分离线程,如果传 ture ,则可创建该线程,并且可将该线程分离,因此,可总结为,FALSE 和 ture 功能为俩点,一创建线程,二将线程分离或不分离
//NULL 为保存出错信息,定义一个 GError **error 类型,类似于 char* ,定义一个指针,取地址传入,可保存出错信息,不保存信息是传入 NULL
//pthread_t tid, tidl;
//pthread_create(&tid, NULL, deal_thread, label).
/inthread_create(&tid1, NULL, deal_thread1, label1)
//如果使用 pthread_create ,创建 tid , tidl 传到线程1的回调函数和线程2的回调函数,再将label和label1这俩个标识的俩个指针传给线程函数;
用pthread_create 创建的函数线程不分离,若希望线程分离可调用 pthread_deat
}