Gobject tutorial 十

news2024/11/24 4:38:41

参考:GLib – 2.0: The Main Event Loop

The Main Event Loop

主事件循环管理所有可用的事件源,事件可以是各种类型、各种数量的。比如说文件描述符(普通文件、管道以及套接字)和超时。

新类型的事件源可以通过函数g_source_attach来添加。为了使多个相互独立的事件源集能在不同的线程中进行处理,每个事件源都会关联一个GMainContext。一个GMainContext只能在一个线程中运行,但, 事件源可以添加到一个线程中的GmainContext,而从另外一个线程中移除。

我们来先来看看GLib中对事件源的定义。

struct _GSource
{
  /*< private >*/
  gpointer callback_data;
  GSourceCallbackFuncs *callback_funcs;

  const GSourceFuncs *source_funcs;
  guint ref_count;

  GMainContext *context;

  gint priority;
  guint flags;
  guint source_id;

  GSList *poll_fds;
  
  GSource *prev;
  GSource *next;

  char    *name;

  GSourcePrivate *priv;
};

由定义可以看到,事件源的定义中包含 GMainContext,结构体中还包含成员priority,这表示每个事件源都被指定一个优先级,默认的优先级是G_PRIORITY_DEFAULT,其值为0,当优先级的值小于0表示高优先级,高优先级的事件源优先处理。

GMainLoop 用于表示主事件循环。它由函数g_main_loop_new()函数创建。在为其添加完事件后,调用函数g_main_loop_run()来运行主事件循环。从代码层面上看,GMainLoop就是一个loop,在loop中它,循环会运行GMainContext来持续检查事件源中的事件并对事件进行分发。最终,会有一个事件源中的事件导致函数g_main_loop_quit()被调用,这就意味着程序退出主循环,函数g_main_loop_run()返回。

我们看一下GLib中GMainLoop是如何定义的。

struct _GMainLoop
{
  GMainContext *context;
  gboolean is_running; /* (atomic) */
  gint ref_count;  /* (atomic) */
};
/**
 * g_main_loop_run:
 * @loop: a #GMainLoop
 * 
 * Runs a main loop until g_main_loop_quit() is called on the loop.
 * If this is called for the thread of the loop's #GMainContext,
 * it will process events from the loop, otherwise it will
 * simply wait.
 **/
void 
g_main_loop_run (GMainLoop *loop)
{
......
  g_atomic_int_set (&loop->is_running, TRUE);
  while (g_atomic_int_get (&loop->is_running))
    g_main_context_iterate_unlocked (loop->context, TRUE, TRUE, self);
......
}

 Creating new source types

GMainLoop允许创建和使用不同于GLib中内置的事件源类型的事件源。

新的事件源类型要继承自GSource结构,与GObject中的有继承关系的数据结构的成员的写法类似,新的事件源类型的数据结构的第一个成员是GSource。创建新的事件源类型实例是通过函数g_source_new()实现的。函数的第一个参数GsourceFuncs中的函数决定了新事件源类型的功能。

GLIB_AVAILABLE_IN_ALL
GSource *g_source_new             (GSourceFuncs   *source_funcs,
                                   guint           struct_size);

我们举例看看在GTK中,新事件源类型数据结构的定义情况。

typedef struct _GdkMacosEventSource
{
  GSource     source;
  GdkDisplay *display;
} GdkMacosEventSource;

 新的事件源类型与其所属GMainContext之间有两种交互方式。第一种方式是通过GSourceFuncs结构中的prepare函数设置一个超时,来指定在主事件循环在检查此事件源之前最大的睡眠时间,另一种方式是,事件源通过调用函数g_source_add_poll()将文件描述符添加到集合中,GMainContext会对集合进行检查。

GSourceFuncs结构的定义如下:

/**
 * GSourceDummyMarshal:
 *
 * This is just a placeholder for #GClosureMarshal,
 * which cannot be used here for dependency reasons.
 */
typedef void (*GSourceDummyMarshal) (void);

struct _GSourceFuncs
{
  gboolean (*prepare)  (GSource    *source,
                        gint       *timeout_);/* Can be NULL */
  gboolean (*check)    (GSource    *source);/* Can be NULL */
  gboolean (*dispatch) (GSource    *source,
                        GSourceFunc callback,
                        gpointer    user_data);
  void     (*finalize) (GSource    *source); /* Can be NULL */

  /*< private >*/
  /* For use by g_source_set_closure */
  GSourceFunc     closure_callback;        
  GSourceDummyMarshal closure_marshal; /* Really is of type GClosureMarshal */
};

Customizing the main loop iteration

函数g_main_context_iteration()就能实现单纯的GMainContext迭代。然而,很多时候,我们需要对主事件循环的运行增加精确控制。比如说,GMainLoop在迭代时使用了另外一个主事件循环,此时,你可以调用g_main_context_iteration()的组成函数g_main_context_prepare(), g_main_context_query(), g_main_context_check() and g_main_context_dispatch()。

State of a Main Context

MainContext的状态图如下:

 Main Contexts

What is GMainContext?

GMainContexts是对事件循环的一个通用实现。一个GMaintext会有多个事件源与之相关,每个事件源都可以被认为是一个拥有回调函数的事件,当事件发生时,回调函数就会执行,也可以认为是一个待检测的文件描述符集。例如,超时可以是一个事件,从套接字上首受到的数据也可以是一个事件。

我们来看看GMainContext的定义。

struct _GMainContext
{
  /* The following lock is used for both the list of sources
   * and the list of poll records
   */
  GMutex mutex;
  GCond cond;
  GThread *owner;
  guint owner_count;
  GMainContextFlags flags;
  GSList *waiters;

  gint ref_count;  /* (atomic) */

  GHashTable *sources;              /* guint -> GSource */

  GPtrArray *pending_dispatches;
  gint timeout;			/* Timeout for current iteration */

  guint next_id;
  GList *source_lists;
  gint in_check_or_prepare;

  GPollRec *poll_records;
  guint n_poll_records;
  GPollFD *cached_poll_array;
  guint cached_poll_array_size;

  GWakeup *wakeup;

  GPollFD wake_up_rec;

/* Flag indicating whether the set of fd's changed during a poll */
  gboolean poll_changed;

  GPollFunc poll_func;

  gint64   time;
  gboolean time_is_fresh;
};

一个完整的事件循环会经过一下几个步骤,如上图所示:

  1. 准备事件源。这个步骤用于确定事件源中是否有准备好立即分发事件的事件源

  2.监听事件源。阻塞当前线程,直到事件源中有事件发生。

  3.检查哪个源中有事件发生。

  4.从事件源中分发回调函数。

对于上述的步骤,我们来看看GLib中的实现。

/**
 * g_main_context_iteration:
 * @context: (nullable): a #GMainContext (if %NULL, the global-default
 *   main context will be used)
 * @may_block: whether the call may block.
 *
 * Runs a single iteration for the given main loop. This involves
 * checking to see if any event sources are ready to be processed,
 * then if no events sources are ready and @may_block is %TRUE, waiting
 * for a source to become ready, then dispatching the highest priority
 * events sources that are ready. Otherwise, if @may_block is %FALSE
 * sources are not waited to become ready, only those highest priority
 * events sources will be dispatched (if any), that are ready at this
 * given moment without further waiting.
 *
 * Note that even when @may_block is %TRUE, it is still possible for
 * g_main_context_iteration() to return %FALSE, since the wait may
 * be interrupted for other reasons than an event source becoming ready.
 *
 * Returns: %TRUE if events were dispatched.
 **/
gboolean
g_main_context_iteration (GMainContext *context, gboolean may_block)
{
  gboolean retval;

  if (!context)
    context = g_main_context_default();
  
  LOCK_CONTEXT (context);
  retval = g_main_context_iterate_unlocked (context, may_block, TRUE, G_THREAD_SELF);
  UNLOCK_CONTEXT (context);
  
  return retval;
}


/* HOLDS context lock */
static gboolean
g_main_context_iterate_unlocked (GMainContext *context,
                                 gboolean      block,
                                 gboolean      dispatch,
                                 GThread      *self)
{
......

  g_main_context_prepare_unlocked (context, &max_priority);
  
  while ((nfds = g_main_context_query_unlocked (
            context, max_priority, &timeout, fds,
            allocated_nfds)) > allocated_nfds)
    {
......
    }

......
  g_main_context_poll_unlocked (context, timeout, max_priority, fds, nfds);
  
  some_ready = g_main_context_check_unlocked (context, max_priority, fds, nfds);
  
  if (dispatch)
    g_main_context_dispatch_unlocked (context);
  
......

  return some_ready;
}

GMainContext的核心,其实就是一个poll()循环,prepare函数作为循环的先导,check和disapatch函数作为后续。

static void
g_main_context_poll_unlocked (GMainContext *context,
                              int           timeout,
                              int           priority,
                              GPollFD      *fds,
                              int           n_fds)
{
......

      poll_func = context->poll_func;
       ......
}

在用户没有调用函数g_main_context_set_poll_func设置poll_func时,GLib的默认poll_func为g_poll.

void
g_main_context_set_poll_func (GMainContext *context,
			      GPollFunc     func)
{
  if (!context)
    context = g_main_context_default ();
  
  g_return_if_fail (g_atomic_int_get (&context->ref_count) > 0);

  LOCK_CONTEXT (context);
  
  if (func)
    context->poll_func = func;
  else
    context->poll_func = g_poll;

  UNLOCK_CONTEXT (context);
}


/**
 * g_poll:
 * @fds: file descriptors to poll
 * @nfds: the number of file descriptors in @fds
 * @timeout: amount of time to wait, in milliseconds, or -1 to wait forever
 *
 * Polls @fds, as with the poll() system call, but portably. (On
 * systems that don't have poll(), it is emulated using select().)
 * This is used internally by #GMainContext, but it can be called
 * directly if you need to block until a file descriptor is ready, but
 * don't want to run the full main loop.
 *
 * Each element of @fds is a #GPollFD describing a single file
 * descriptor to poll. The @fd field indicates the file descriptor,
 * and the @events field indicates the events to poll for. On return,
 * the @revents fields will be filled with the events that actually
 * occurred.
 *
 * On POSIX systems, the file descriptors in @fds can be any sort of
 * file descriptor, but the situation is much more complicated on
 * Windows. If you need to use g_poll() in code that has to run on
 * Windows, the easiest solution is to construct all of your
 * #GPollFDs with g_io_channel_win32_make_pollfd().
 *
 * Returns: the number of entries in @fds whose @revents fields
 * were filled in, or 0 if the operation timed out, or -1 on error or
 * if the call was interrupted.
 *
 * Since: 2.20
 **/
gint
g_poll (GPollFD *fds,
	guint    nfds,
	gint     timeout)
{
  return poll ((struct pollfd *)fds, nfds, timeout);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1862391.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

输入/输出文字

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 在海龟绘图中&#xff0c;也可以输入或者输出文字&#xff0c;下面分别进行介绍。 1 输出文字 输出文字可以使用write()方法来实现&#xff0c;语…

【QCustomPlot实战系列】QCPGraph区域高亮

使用QCPDataSelection来设置选中的区域&#xff0c;并将QCPGraph的可选择区域设置成QCP::stMultipleDataRanges void AreaPieces::initCustomPlot(QCustomPlot *parentPlot) {QVector<double> x {0, 1, 2, 3, 4, 5, 6, 7, 8};QVector<double> y {200, 560, 750…

解决Vue+Vite打包后Leaflet的marker图标不显示的问题

前言 用Leaflet写关于WebGIS的开发&#xff0c;用Vite或者webpack打包&#xff0c;打包后会找不到图标&#xff0c;如下所示。 直言的说&#xff0c;笔者去网上搜了搜&#xff0c;其实收到一个比较好是答案。网址如下。 &#xff08;完美解决~&#xff09;关于VueLeaflet添加…

P2实验室装修标准都有哪些

P2实验室&#xff08;也称为生物安全二级实验室&#xff0c;BSL-2实验室&#xff09;的装修标准需要满足一系列的设计和施工要求&#xff0c;以确保实验室的安全性和功能性。因此&#xff0c;P2实验室装修标准不仅要满足一般实验室的要求&#xff0c;还需符合生物安全的特殊规定…

企业运维六边形战士 质量稳定 效率为王

随着信息化的不断深入和扩展&#xff0c;企业IT系统的复杂性和设备多样性日益增加。为了保障业务的高可用性和连续性&#xff0c;企业需要一个全面、高效、智能的一体化运维管理平台。在用户市场的推动下&#xff0c;LinkSLA智能运维管家展现出【六边形战士】的优质属性&#x…

数据结构-----【链表:刷题】

-------------------------------------------基础题参照leetcode---------------------------------------------------------------------------------------------------------- 【2】两数相加 /*** Definition for singly-linked list.* struct ListNode {* int val;…

ChatBI开源实现: 基于SuperSonic的AI+BI的产品设计

产品起源 为什么要做这样的产品&#xff1f;文章《ChatBI开源实现: AIBI的产品设计》中有介绍 为什么要自己做这样的产品&#xff1f;1、低成本试错&#xff1b;2、未来数据生态入口&#xff1b; 为什么要基于Supersonic做&#xff1f; 开源协议友好&#xff1a;可魔改商用 社区…

仿真分析 + AI:创建基础设施的新未来

吴付标 大漠风电&#xff0c;深远海平台&#xff0c;一带一路上的高铁、电站…… 在新一轮的基础设施建设浪潮中&#xff0c;项目与结构变得越来越复杂&#xff0c;碳中和与可持续发展的要求越来越高&#xff0c;仿真分析技术应运而兴。 在最近召开的Bentley软件2024创新智旅 …

【Python/Pytorch - 网络模型】-- 高阶SVD算法

文章目录 文章目录 00 写在前面01 基于Python版本的高阶SVD算代码02 HOSVD 的步骤 00 写在前面 高阶奇异值分解&#xff08;Higher-Order SVD&#xff0c;HOSVD&#xff09;是一种将传统的奇异值分解&#xff08;SVD&#xff09;扩展到高阶张量的方法。它能够将一个高阶张量分…

FileNotFoundError: Cannot find DGL C++ graphbolt library at ...

FileNotFoundError: Cannot find DGL C graphbolt library at ...-CSDN博客https://blog.csdn.net/weixin_44017989/article/details/137658749

SAP ABAP 之OOALV

文章目录 前言一、案例介绍/笔者需求二、SE24 查看类 a.基本属性 Properties b.接口 Interfaces c.友元 Friends d.属性 Attributes e.方法 Methods f.事件 Events g.局部类型 Types …

韩顺平0基础学java——第31天

p612-637 IO流 IO流原理及流的分类 Java lO流原理 1.I/O是Input/Output的缩弓&#xff0c;IV/O技术是非常实用的技术&#xff0c;用于处理数据传输。 如读/写文件&#xff0c;网络通讯等。 2. Java程序中&#xff0c;对于数据的输入/输出操作以”流(stream)”的方式进行。 3…

Orangepi Zero2 全志H616 的初识

目录 一、全志H616简介 1.1 为什么学&#xff1a; 1.2 学什么&#xff1a; 1.3 全志H616平台介绍&#xff1a; 二、刷机 系统启动和初始化配置 2.1 需要的工具&#xff1a; 2.2 工具安装&#xff1a; 2.3 刷机&#xff1a; 2.4 登录系统&#xff1a; 2.5 修改登录密码…

Android开发系列(十)Jetpack Compose之Card

Card是一种常用的UI组件&#xff0c;用于显示一个具有卡片样式的容器。Card组件通常用于显示列表项、卡片式布局或任何需要显示边框和阴影的UI元素。 使用Card组件&#xff0c;您可以轻松地创建带有卡片效果的UI元素。以下是一些Card组件的常见属性和功能&#xff1a; elevati…

云计算基础知识

前言&#xff1a; 随着ICT技术的高速发展&#xff0c;企业架构对计算、存储、网络资源的需求更高&#xff0c;急需一种新的架构来承载业务&#xff0c;以获得持续&#xff0c;高速&#xff0c;高效的发展&#xff0c;云计算应运而生。 云计算背景 信息大爆炸时代&#xff1a…

导出 S 参数扫描结果供 INTERCONNECT 使用

导出 S 参数扫描结果供 INTERCONNECT 使用 正文正文 有时候,对于 FDTD 无法直接进行仿真的大型仿真链路,我们需要使用 FDTD 针对单个小的模块进行仿真,再将得到的 S 参数结果导入到 INTERCONNECT 中使用,最终完成整个链路的仿真。通常完成 S 参数扫描后其状态如下图所示:…

CSS 原生属性 CSS文本显示省略号...

效果图&#xff1a; 直接上代码 .header {width: 100%;//使用百分比或者固定宽度&#xff0c;注意使用百分比时要确保父元素使用了百分比宽度height: 50px;line-height: 25px;font-weight: bolder;text-overflow: ellipsis;word-break: break-all;overflow: hidden;display: -…

第3章 小功能大用处-事务与Lua

为了保证多条命令组合的原子性&#xff0c;Redis提供了简单的事务功能以及集成Lua脚本来解决这个问题。 首先简单介绍Redis中事务的使用方法以及它的局限性&#xff0c;之后重点介绍Lua语言的基本使用方法&#xff0c;以及如何将Redis和Lua脚本进行集成&#xff0c;最后给出Red…

圈复杂度.

圈复杂度是衡量代码的重要标准 配置&#xff1a; eslint里面&#xff1a;rules&#xff1a;complexity&#xff1a;[error,10]