主事件循环-管理所有可用的事件源

主事件循环管理 GLib 和 GTK+ 应用程序的所有可用事件源。这些事件可以来自任意数量的不同类型的源,例如文件描述符(普通文件、管道或套接字)和超时。还可以使用 g_source_attach() 添加新类型的事件源。

为了允许在不同的线程中处理多个独立的源代码集,每个源代码都与一个文本相关联。

GMainContext 只能在单个线程中运行,但可以向其中添加源代码,也可以从其他线程中删除源代码。

每个事件源都有一个优先级。默认优先级 G_PRIORITY_DEFAULT 为 0。小于 0 的值表示优先级更高。大于 0 的值表示优先级较低。来自高优先级源的事件总是在来自低优先级源的事件之前处理。

还可以添加空闲函数,并为其分配优先级。每当没有更高优先级的事件准备好处理时,就会运行这些操作。

GMainLoop 数据类型表示主事件循环。GMainLoop 是用  g_main_loop_new() 创建的。添加初始事件源后,将调用  g_main_loop_run()。这将持续检查来自每个事件源的新事件并对其进行分派。最后,对来自其中一个源的事件的处理导致调用 g_main_loop_quit() 退出主循环,并返回 g_main_loop_run()

可以递归地创建 GMainLoop 的新实例。在 GTK+ 应用程序中,当显示模式对话框时,经常使用这种方法。注意,事件源与特定的 GMainContext 关联,将检查和调度与该 GMainContext 相关的所有主循环。

GTK+包含其中一些函数的包装器,例如 gtk_main()、gtk_main_quit() 和 gtk_events_pending()。

创建新的源类型

GMainLoop 功能的一个不寻常的特性是,除了内置类型的事件源之外,还可以创建和使用新类型的事件源。新的事件源类型用于处理 GDK 事件。通过从 GSource 结构“派生”来创建新的源类型。源的派生类型由将 GSource 结构作为第一个元素的结构以及特定于新源类型的其他元素表示。要创建新源类型的实例,请调用 g_source_new(),传入派生结构的大小和函数表。这些GSourceFuncs 确定新源类型的行为。

新的源类型基本上以两种方式与主上下文交互。GSourceFuncs中的prepare函数可以设置一个超时,以确定在再次检查源之前主循环将休眠的最长时间。此外,源代码还可以使用g\u source\u add\u poll()将文件描述符添加到主上下文检查的集合中。

自定义主循环迭代

GMainContext的单个迭代可以使用 g_main_context_iteration() 运行。在某些情况下,需要更详细地控制主循环的具体工作方式,例如,在 将GMainLoop 与外部主循环集成时。在这种情况下,您可以直接调用 g_main_context_iteration() 的组件函数。这些函数是 g_main_context_prepare()g_main_context_query()g_main_context_check() 和 g_main_context_dispatch()

主上下文的状态

这些函数的操作可以最好地用状态图来表示,如图所示。

在 UNIX 上,GLib mainloop 与 fork() 不兼容。任何使用 mainloop 的程序都必须在不返回 mainloop 的情况下从子级 exec() 或 exit()。

源的内存管理

对于传递给 GSource 的用户数据的内存管理,有两个选项可以在调用时传递给它的回调。这些数据是在对 g_timeout_add()g_timeout_add_full()g_idle_add() 等的调用中提供的,更一般地说,是在使用  g_source_set_callback() 时提供的。此数据通常是“拥有”超时或空闲回调的对象,例如小部件或网络协议实现。在许多情况下,在销毁拥有的对象之后调用回调是错误的,因为这会导致使用释放的内存。

第一个也是首选的选项是存储由 g_timeout_add() 或 g_source_attach() 等函数返回的源ID,并在完成所属对象时使用  g_source_remove() 从主上下文中显式删除该源。这确保了回调只能在对象仍处于活动状态时调用。

第二个选项是在回调中保持对对象的强引用,并在回调的 GDestroyNotify 中释放它。这确保了对象在源代码完成之前保持活动状态,而在最后一次调用它之后,也就是说保证是活动的。

GDestroyNotify 是传递给 GSource 函数的 “full” 变体的另一个回调(例如,g_timeout_add_full())。它是在源代码完成时调用的,并且是为发布这样的引用而设计的。

第二种方法的一个重要警告是,如果在调用 GSource 之前停止主循环,那么它将无限期地保持对象的活动状态,这可能是不需要的。

线程

线程-对线程、互斥锁、锁、条件和线程私有数据的可移植支持

线程的行为几乎与进程类似,但与进程不同的是,一个进程的所有线程共享相同的内存。这是好的,因为它通过共享内存在相关线程之间提供了方便的通信,而这是坏的,因为如果程序设计得不仔细,可能会发生奇怪的事情(所谓的“heisenbug”)。特别是,由于线程的并发性,不能对在不同线程中运行的代码的执行顺序进行任何假设,除非程序员通过同步原语显式地强制执行顺序。

GLib中线程相关函数的目的是为编写多线程软件提供一种可移植的方法。有用于互斥的原语来保护对部分内存的访问(GMutex, GRecMutex 和 GRWLock)。有一种工具可以将单个位用于锁(g_bit_lock())。有条件变量的原语允许线程同步(GCond)。有线程私有数据的原语—每个线程都有一个私有实例(GPrivate)的数据。有用于一次性初始化的工具(GOnce, g_once_init_enter()) 。最后,还有创建和管理线程的原语(GThread)。

GLib 线程系统以前是用 g_thread_init() 初始化的。这已经没有必要了。从版本 2.32 开始,GLib线程系统会在程序开始时自动初始化,所有线程创建函数和同步原语都可以立即使用。

请注意,即使您自己不调用 g_thread_new(),假设程序没有线程也是不安全的。GLib和GIO可以并且将在某些情况下为自己的目的创建线程,例如使用  g_unix_signal_source_new() 或使用 GDBus 时。

最初,UNIX 没有线程,因此一些传统的 unixapi 在线程程序中存在问题。一些显著的例子是

  • 在静态分配的缓冲区中返回数据的C库函数,如strtok() ​​​​或 strerror()。对于其中的许多,有带有 _r 后缀的线程安全变体,或者您可以查看相应的 GLib APIs (比如 g_strsplit() 或  g_strerror())。
  • 函数 setenv() 和 unsetenv() 以非线程安全的方式操作进程环境,并且可能会干扰其他线程中的 getenv() 调用。请注意,getenv() 调用可能隐藏在其他API后面。例如,GNU gettext() 在封面下调用getenv()。一般来说,最好将环境视为只读。如果您必须修改环境,请在main() 中尽早进行,此时还没有其他线程。
  • 函数  setlocale() 的作用是:更改整个进程的区域设置,从而影响所有线程。对区域设置的临时更改通常是为了更改字符串扫描或格式化函数(如 scanf() 或 printf() )的行为。GLib提供了许多字符串 api(如 g_ascii_formatd()g_ascii_strtod()),这些 api 通常可以作为替代。或者可以使用 uselocale()  函数仅更改当前线程的区域设置。
  • fork() 函数只将调用线程带入子进程映像的副本中。如果其他线程在关键部分执行,它们可能会使互斥锁处于锁定状态,这很容易导致新子线程出现死锁。因此,应该尽快在子对象中调用exit() 或exec(),并且在此之前只进行信号安全库调用。
  • daemon() 函数使用 fork() 的方式与上面描述的相反。它不应该与GLib程序一起使用。

GLib 本身在内部是完全线程安全的(所有全局数据都会自动锁定),但由于性能原因,个别数据结构实例不会自动锁定。例如,必须协调多个线程对同一个GHashTable的访问。此规则的两个显著例外是GMainLoop和GAsyncQueue,它们是线程安全的,不需要进一步的应用程序级锁定就可以从多个线程访问。大多数引用计数函数,如g\u object\u ref()也是线程安全的。

GThreads的一个常见用法是将长时间运行的阻塞操作从主线程移到工作线程。对于GLib函数,例如单GIO操作,这是不必要的,并且使代码复杂化。相反,应该从主线程使用函数的 …_async() 版本,这样就不需要在多个线程之间进行锁定和同步。如果一个操作需要移动到一个工作者线程中,考虑使用 g_task_run_in_thread(),或者 GThreadPool。GThreadPool 通常是比GThread 更好的选择,因为它处理线程重用和任务排队;GTask在内部使用它。

但是,如果需要按顺序执行多个阻塞操作,并且不可能对它们使用GTask,那么将它们移动到工作线程可以澄清代码。

线程池

线程池—并发执行工作的线程池

有时您希望异步地分叉工作的执行并继续在自己的线程中工作。如果这种情况经常发生,那么每次启动和销毁线程的开销可能太大。在这种情况下,重用已经启动的线程似乎是个好主意。确实如此,但实现这一点可能是乏味和容易出错的。

因此GLib为您提供了线程池。另外一个优点是,当程序的不同子系统使用GLib时,线程可以在它们之间共享。

异步队列

异步队列-线程之间的异步通信

通常需要在不同的线程之间进行通信。一般来说,不通过共享内存而通过显式消息传递来实现这一点更安全。不过,这些消息只对多线程应用程序异步有意义,因为同步操作也可以在同一线程中完成。

异步队列是大多数其他GLib数据结构的一个例外,因为它们可以从多个线程中同时使用,而无需显式锁定,并且它们带来了自己的内置引用计数。这是因为异步队列的本质是它将始终由至少2个并发线程使用。

模块动态加载

模块动态加载.动态加载“插件”的便携式方法

这些函数提供了一种可移植的方式来动态加载对象文件(通常称为“插件”)。当前的实现支持提供dlopen() 实现的所有系统(例如Linux/Sun),以及通过dll的Windows平台。

要使用这些函数的程序必须链接到 pkg-config --libs gmodule-2.0 命令输出的库。

内存分配

内存分配-常规内存处理

这些函数支持分配和释放内存。

内存片

内存片—分配大小相等的内存块组的有效方法

内存片提供了一种节省空间和多处理可伸缩的方式来分配大小相等的内存块,就像最初的GMemChunks(来自glib2.8)一样,同时避免了它们过度的内存浪费、可伸缩性和性能问题。

为了实现这些目标,slice分配器使用了一个复杂的分层设计,这个设计的灵感来自于Bonwick的slab分配器(Bonwick94-Jeff-Bonwick,slab分配器:一个对象缓存内核内存分配器)。USENIX 1994和Bonwick 01,Bonwick和Jonathan Adams,杂志和vmem:将slab分配器扩展到许多cpu和任意资源。USENIX 2001年)

它使用 posix_memalign() 来优化许多大小相等的块的分配,并具有每个线程的空闲列表(所谓的库层)来快速满足已知结构大小的分配请求。这伴随着额外的缓存逻辑,以便在将释放的内存返回系统之前将其保留一段时间。由于对齐约束而未使用的内存用于缓存着色(块地址的随机分布),以提高CPU缓存利用率。片分配器的缓存层适应高锁争用,以提高可伸缩性。

切片分配器可以分配小到两个指针的块,并且与 malloc() 不同,它不会为每个块保留额外的空间。对于较大的块大小,g_slice_new() 和 g_slice_alloc() 将自动委托给系统 malloc() 实现。对于新编写的代码,建议使用新的 g_slice API,而不是 g_malloc() 和 friends,只要对象在其生存期内没有调整大小,并且释放时分配时使用的对象大小仍然可用。

IO通道

IO通道-使用文件、管道和套接字的便携式支持

GIOChannel 数据类型旨在提供一种可移植的方法,用于使用文件描述符、管道和套接字,并将它们集成到主事件循环中。目前,UNIX平台上提供了完全支持,对Windows的支持仅部分完成。

错误报告

错误报告-报告错误的系统

GLib提供了一种标准的方法来报告从被调用函数到调用代码的错误(这与其他语言中的异常解决的问题相同。)理解此方法既是数据类型(GError结构)又是一组规则非常重要。如果您不正确地使用GError,那么您的代码将无法与使用GError的其他代码正确地互操作,并且您的API的用户可能会感到困惑。在大多数情况下,使用GError比使用数字错误代码更可取,但在某些情况下,数字错误代码对性能很有用。

警告和断言

略。

消息输出和调试函数

消息输出和调试函数-用于输出消息和帮助调试应用程序的函数

GLib核心应用支持相关推荐

  1. MT6853(联发科技天玑 720 )核心板支持5G北斗

    1.产品概述 该款MT6853核心板是一款针对AI人工智能的一款高性能AI安卓主板, 基于 MTK 的 MT6853(联发科技天玑 720 )平台.工业级高性能.可运行 android 11.0操作系 ...

  2. MongoDB 核心将支持全文搜索功能 (2.3.2)

    来自 MongoDB 官方 JIRA 的一个新特性报告称 MongoDB 将在 2.3.2 版本中增加全文搜索功能.该功能还是体验到阶段,使用方法包括: db.adminCommand( { setP ...

  3. modbus软件开发实战指南_C++核心准则?GSL:指南支持库

    GSL: Guidelines support library GSL:指南支持库 The GSL is a small library of facilities designed to suppo ...

  4. 全志A40i工业核心板,100%国产4核ARM Cortex-A7,支持“双屏异显”【显示接口能力,工业HMI首选方案】

    1 多核国产工业平台,支持国产ARM处理器发展 创龙科技SOM-TLA40i是一款基于全志科技A40i处理器设计的4核ARM Cortex-A7国产工业核心板,每核主频高达1.2GHz. 图 1 SO ...

  5. RK3288核心板金手指接插方式支持 Android5.1 Android7.1、Linux 操作系统

          DLT-K1核心板采用 Rockchip RK3288四核 Cortex-A17 1.8GHz 处理器,核心板采用 6 层工艺设计,核心板出PIN采用金手指方式,通过标准DDR4座子与底板 ...

  6. linux安装最新版的glib库,GLib库安装与简析

    GLib是GTK+和GNOME工程的基础底层核心程序库,是一个综合用途的实用的轻量级的C程序库, 它提供C语言的常用的数据结构的定义.相关的处理函数,有趣而实用的宏, 可移植的封装和一些运行时机能,如 ...

  7. php 长连接心跳_支持gRPC长链接,深度解读Nacos2.0架构设计及新模型

    作者 | 杨翊(席翁) Nacos PMC 来源|阿里巴巴云原生公众号 Nacos 简介 Nacos 在阿里巴巴起源于 2008 年五彩石项目,该项目完成了微服务拆分和业务中台建设,随着云计算和开源环 ...

  8. 支持 gRPC 长链接,深度解读 Nacos 2.0 架构设计及新模型

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 作者 | 杨翊(席翁)  Nacos PMC Nacos ...

  9. Java还欠缺什么才能真正支持机器/深度学习?

    如何让团队开始使用ML以及如何最好地将ML与我们运行的现有系统集成? 实际上没有用Java构建的ML框架(有DL4J,但我真的不知道有谁使用它,MXNet有一个Scala API而不是Java,而且它 ...

最新文章

  1. win7 系统盘下AppData文件夹中Local和Roaming分别有什么作用?
  2. WPF无边框拖动、全屏、缩放
  3. [shell问答录]:命令、进程、子shell...
  4. ZOJ 2675 Little Mammoth(计算几何)
  5. 快速排序算法 java 实现
  6. 数学--数论-- HDU6298 Maximum Multiple 打表找规律
  7. 第四章、项目整合管理【PMP】
  8. C#用SqlCilent模式连接数据库实例
  9. 第二阶段冲刺10天 第二天
  10. Python爬虫基础-02-提取数据
  11. Rust 中 Trait 的使用及实现分析
  12. 微信小程序视频直播开发实现流程
  13. 泰坦尼克号数据_案例三:泰坦尼克号乘客的幸存预测_使用文档_机器数据分析平台...
  14. php utc 北京时间,美国各个时区转换成北京时间 utc gmt
  15. POJ 6184 【三元环 +分治】
  16. vue中的路由及自定义图标
  17. Groovy实战分析
  18. Exp2 后门原理与实践 ——20164316张子遥
  19. 入大数据行业,主要应该要学习什么?
  20. 瑞安航空和曼彻斯特机场集团因国际旅行限制联合起诉英国政府

热门文章

  1. 整理准备使用wireshark、拉米在线解密PDM5生成密钥网址
  2. (BN)批量归一化全面解析
  3. Linux系统开启wifi热点的方法
  4. ZOJ2477 拼魔方
  5. 微信直接用手机默认浏览器打开下载 IOS苹果跳转App Store(苹果商店)打开下载
  6. 腾讯云CityBase产品白皮书 附下载地址
  7. window自带的桌面整理工具
  8. 视频字幕API接口文档
  9. 判断当前音效是否播放完毕
  10. hadoop ls命令