Gtk 线程|学习笔记

简介: 快速学习 Gtk 线程

开发者学堂课程【物联网开发- 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

生成效果图

image.gif这个界面一个窗口中有俩个 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

}

相关文章
|
7月前
|
网络协议 Linux C++
Linux C/C++ 开发(学习笔记十一 ):TCP服务器(并发网络网络编程 一请求一线程)
Linux C/C++ 开发(学习笔记十一 ):TCP服务器(并发网络网络编程 一请求一线程)
149 0
|
2月前
|
编解码 数据安全/隐私保护 计算机视觉
Opencv学习笔记(十):同步和异步(多线程)操作打开海康摄像头
如何使用OpenCV进行同步和异步操作来打开海康摄像头,并提供了相关的代码示例。
124 1
Opencv学习笔记(十):同步和异步(多线程)操作打开海康摄像头
|
2月前
FFmpeg学习笔记(二):多线程rtsp推流和ffplay拉流操作,并储存为多路avi格式的视频
这篇博客主要介绍了如何使用FFmpeg进行多线程RTSP推流和ffplay拉流操作,以及如何将视频流保存为多路AVI格式的视频文件。
358 0
|
4月前
|
调度
多线程学习笔记
这篇文章是多线程学习笔记,涵盖了线程与进程的概念、多线程实现方式、线程状态、线程同步与不安全示例、死锁问题以及生产者与消费者问题等多线程编程的关键知识点。
多线程学习笔记
|
7月前
|
NoSQL 网络协议 关系型数据库
redis-学习笔记(redis 单线程模型)
redis-学习笔记(redis 单线程模型)
63 3
|
7月前
|
安全 Java 编译器
多线程 (下) - 学习笔记2
多线程 (下) - 学习笔记
51 1
|
7月前
|
存储 算法 Java
多线程 (下) - 学习笔记1
多线程 (下) - 学习笔记
53 1
|
7月前
|
设计模式 安全 NoSQL
多线程 (上) - 学习笔记2
多线程 (上) - 学习笔记
52 1
|
7月前
|
Java 数据库连接 程序员
【后台开发】TinyWebser学习笔记(2)线程池、数据库连接池
【后台开发】TinyWebser学习笔记(2)线程池、数据库连接池
80 4
|
7月前
多线程学习笔记(一)
创建线程有3种方式:继承Thread类、实现Runnable接口或Callable接口。继承Thread类时,重写run()方法并调用start()启动线程。实现Runnable接口时,实现run()方法,通过Thread的target创建线程对象并用start()启动。
40 1