1. 程式人生 > >glib學習筆記之二(續)——GLib核心應用支援:The Main Event Loop

glib學習筆記之二(續)——GLib核心應用支援:The Main Event Loop

原文地址

描述

The main event loop manages all the available sources of events for GLib and GTK+ applications. These events can come from any number of different types of sources such as file descriptors (plain files, pipes or sockets) and timeouts. New types of event sources can also be added using g_source_attach().

To allow multiple independent sets of sources to be handled in different threads, each source is associated with a GMainContext. A GMainContext can only be running in a single thread, but sources can be added to it and removed from it from other threads.

Each event source is assigned a priority. The default priority, G_PRIORITY_DEFAULT, is 0. Values less than 0 denote higher priorities. Values greater than 0 denote lower priorities. Events from high priority sources are always processed before events from lower priority sources.

Idle functions can also be added, and assigned a priority. These will be run whenever no events with a higher priority are ready to be processed.

The GMainLoop data type represents a main event loop. A GMainLoop is created with g_main_loop_new(). After adding the initial event sources, g_main_loop_run() is called. This continuously checks for new events from each of the event sources and dispatches them. Finally, the processing of an event from one of the sources leads to a call to g_main_loop_quit() to exit the main loop, and g_main_loop_run() returns.

It is possible to create new instances of GMainLoop recursively. This is often used in GTK+ applications when showing modal dialog boxes. Note that event sources are associated with a particular GMainContext, and will be checked and dispatched for all main loops associated with that GMainContext.

解釋

GMainContext 代表一個執行緒的上下文。一個 GMainContext 可以關聯多個 GMainLoop,但是這些 GMainLoop 必須在一個執行緒中使用,也就是說這些 GMainLoop 必須是一個巢狀在另一個之中。

GMainLoop 是用 select 或 poll 來實現的。GMainLoop 可以定義事件源的優先順序,純 select 或 poll 函式沒有提供這樣的功能。另外,GMainLoop 可以處理多個計時器。自己用 select 或 poll 函式來實現的話需要做額外的工作(維護一個計時器佇列)。

例子

下面給出一個完整的例子程式 (glib/test 中的 mainloop-test.c)。

注意把一個檔案描述符加到 context 中的方法:

  adder_source = g_io_create_watch (channels[0], G_IO_IN | G_IO_HUP);
  g_source_set_callback (adder_source, (GSourceFunc)adder_callback, &addr_data, NULL);
  g_source_attach (adder_source, context);
  g_source_unref (adder_source);
 
  timeout_source = g_timeout_source_new (10);
  g_source_set_callback (timeout_source, (GSourceFunc)timeout_callback, &addr_data, NULL);
  g_source_set_priority (timeout_source, G_PRIORITY_HIGH);
  g_source_attach (timeout_source, context);
  g_source_unref (timeout_source);


  • 有 n 個 context,每個 context 中有個 Adder。
  • 每一個 crawler 隨機和一個 context 相關聯。Crawler 並不影響 Adder。
#undef G_DISABLE_ASSERT
#undef G_LOG_DOMAIN
 
#include <errno.h>
#include <glib.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
 
#define ITERS 10000
#define INCREMENT 10
#define NTHREADS 4
#define NCRAWLERS 4
#define CRAWLER_TIMEOUT_RANGE 40
#define RECURSER_TIMEOUT 50
 
/* The partial ordering between the context array mutex and
 * crawler array mutex is that the crawler array mutex cannot
 * be locked while the context array mutex is locked
 */
GPtrArray *context_array;
GMutex *context_array_mutex;
GCond *context_array_cond;
 
GMainLoop *main_loop;
 
G_LOCK_DEFINE_STATIC (crawler_array_lock);
GPtrArray *crawler_array;
 
typedef struct _AddrData AddrData;
typedef struct _TestData TestData;
 
struct _AddrData
{
  GMainLoop *loop;
  GIOChannel *dest;
  gint count;
};
 
struct _TestData
{
  gint current_val;
  gint iters;
  GIOChannel *in;
};
 
static void cleanup_crawlers (GMainContext *context);
 
gboolean
read_all (GIOChannel *channel, char *buf, gsize len)
{
  gsize bytes_read = 0;
  gsize count;
  GIOError err;
 
  while (bytes_read < len)
    {
      err = g_io_channel_read (channel, buf + bytes_read, len - bytes_read, &count);
      if (err)
	{
	  if (err != G_IO_ERROR_AGAIN)
	    return FALSE;
	}
      else if (count == 0)
	return FALSE;
 
      bytes_read += count;
    }
 
  return TRUE;
}
 
gboolean
write_all (GIOChannel *channel, char *buf, gsize len)
{
  gsize bytes_written = 0;
  gsize count;
  GIOError err;
 
  while (bytes_written < len)
    {
      err = g_io_channel_write (channel, buf + bytes_written, len - bytes_written, &count);
      if (err && err != G_IO_ERROR_AGAIN)
	return FALSE;
 
      bytes_written += count;
    }
 
  return TRUE;
}
 
gboolean
adder_callback (GIOChannel   *source,
		GIOCondition  condition,
		gpointer      data)
{
  char buf1[32];
  char buf2[32];
 
  char result[32];
 
  AddrData *addr_data = data;
 
  if (!read_all (source, buf1, 32) ||
      !read_all (source, buf2, 32))
    {
      g_main_loop_quit (addr_data->loop);
      return FALSE;
    }
 
  sprintf (result, "%d", atoi(buf1) + atoi(buf2));
  write_all (addr_data->dest, result, 32);
 
  return TRUE;
}
 
gboolean
timeout_callback (gpointer data)
{
  AddrData *addr_data = data;
 
  addr_data->count++;
 
  return TRUE;
}
 
gpointer
adder_thread (gpointer data)
{
  GMainContext *context;
  GSource *adder_source;
  GSource *timeout_source;
 
  GIOChannel **channels = data;
  AddrData addr_data;
 
  context = g_main_context_new ();
 
  g_mutex_lock (context_array_mutex);
 
  g_ptr_array_add (context_array, context);
 
  if (context_array->len == NTHREADS)
    g_cond_broadcast (context_array_cond);
 
  g_mutex_unlock (context_array_mutex);
 
  addr_data.dest = channels[1];
  addr_data.loop = g_main_loop_new (context, FALSE);
  addr_data.count = 0;
 
  adder_source = g_io_create_watch (channels[0], G_IO_IN | G_IO_HUP);
  g_source_set_callback (adder_source, (GSourceFunc)adder_callback, &addr_data, NULL);
  g_source_attach (adder_source, context);
  g_source_unref (adder_source);
 
  timeout_source = g_timeout_source_new (10);
  g_source_set_callback (timeout_source, (GSourceFunc)timeout_callback, &addr_data, NULL);
  g_source_set_priority (timeout_source, G_PRIORITY_HIGH);
  g_source_attach (timeout_source, context);
  g_source_unref (timeout_source);
 
  g_main_loop_run (addr_data.loop);
 
  g_io_channel_unref (channels[0]);
  g_io_channel_unref (channels[1]);
 
  g_free (channels);
 
  g_main_loop_unref (addr_data.loop);
 
#ifdef VERBOSE
  g_print ("Timeout run %d times\n", addr_data.count);
#endif
 
  g_mutex_lock (context_array_mutex);
  g_ptr_array_remove (context_array, context);
  if (context_array->len == 0)
    g_main_loop_quit (main_loop);
  g_mutex_unlock (context_array_mutex);
 
  cleanup_crawlers (context);
 
  return NULL;
}
 
void
io_pipe (GIOChannel **channels)
{
  gint fds[2];
 
  if (pipe(fds) < 0)
    {
      g_warning ("Cannot create pipe %s\n", g_strerror (errno));
      exit (1);
    }
 
  channels[0] = g_io_channel_unix_new (fds[0]);
  channels[1] = g_io_channel_unix_new (fds[1]);
 
  g_io_channel_set_close_on_unref (channels[0], TRUE);
  g_io_channel_set_close_on_unref (channels[1], TRUE);
}
 
void
do_add (GIOChannel *in, gint a, gint b)
{
  char buf1[32];
  char buf2[32];
 
  sprintf (buf1, "%d", a);
  sprintf (buf2, "%d", b);
 
  write_all (in, buf1, 32);
  write_all (in, buf2, 32);
}
 
gboolean
adder_response (GIOChannel   *source,
		GIOCondition  condition,
		gpointer      data)
{
  char result[32];
  TestData *test_data = data;
 
  if (!read_all (source, result, 32))
    return FALSE;
 
  test_data->current_val = atoi (result);
  test_data->iters--;
 
  if (test_data->iters == 0)
    {
      if (test_data->current_val != ITERS * INCREMENT)
	{
	  g_print ("Addition failed: %d != %d\n",
		   test_data->current_val, ITERS * INCREMENT);
	  exit (1);
	}
 
      g_io_channel_unref (source);
      g_io_channel_unref (test_data->in);
 
      g_free (test_data);
 
      return FALSE;
    }
 
  do_add (test_data->in, test_data->current_val, INCREMENT);
 
  return TRUE;
}
 
void
create_adder_thread (void)
{
  GError *err = NULL;
  TestData *test_data;
 
  GIOChannel *in_channels[2];
  GIOChannel *out_channels[2];
 
  GIOChannel **sub_channels;
 
  sub_channels = g_new (GIOChannel *, 2);
 
  io_pipe (in_channels);
  io_pipe (out_channels);
 
  sub_channels[0] = in_channels[0];
  sub_channels[1] = out_channels[1];
 
  g_thread_create (adder_thread, sub_channels, FALSE, &err);
 
  if (err)
    {
      g_warning ("Cannot create thread: %s", err->message);
      exit (1);
    }
 
  test_data = g_new (TestData, 1);
  test_data->in = in_channels[1];
  test_data->current_val = 0;
  test_data->iters = ITERS;
 
  g_io_add_watch (out_channels[0], G_IO_IN | G_IO_HUP,
		  adder_response, test_data);
 
  do_add (test_data->in, test_data->current_val, INCREMENT);
}
 
static void create_crawler (void);
 
static void
remove_crawler (void)
{
  GSource *other_source;
 
  if (crawler_array->len > 0)
    {
      other_source = crawler_array->pdata[g_random_int_range (0, crawler_array->len)];
      g_source_destroy (other_source);
      g_assert (g_ptr_array_remove_fast (crawler_array, other_source));
    }
}
 
static gint
crawler_callback (gpointer data)
{
  GSource *source = data;       
 
  G_LOCK (crawler_array_lock); /* <-- note, before this crawler lock the
                                * array, it may be removed during the interval */
 
  /* if my corresponding source is all ready been removed, remove another one */
  if (!g_ptr_array_remove_fast (crawler_array, source))
    remove_crawler();
 
  remove_crawler();
  G_UNLOCK (crawler_array_lock);
 
  create_crawler();
  create_crawler();
 
  return FALSE;
}
 
static void
create_crawler (void)
{
  GSource *source = g_timeout_source_new (g_random_int_range (0, CRAWLER_TIMEOUT_RANGE));
  g_source_set_callback (source, (GSourceFunc)crawler_callback, source, NULL);
 
  G_LOCK (crawler_array_lock);
  g_ptr_array_add (crawler_array, source);
 
  g_mutex_lock (context_array_mutex);
  g_source_attach (source, context_array->pdata[g_random_int_range (0, context_array->len)]);
  g_source_unref (source);
  g_mutex_unlock (context_array_mutex);
 
  G_UNLOCK (crawler_array_lock);
}
 
static void
cleanup_crawlers (GMainContext *context)
{
  gint i;
 
  G_LOCK (crawler_array_lock);
  for (i=0; i < crawler_array->len; i++)
    {
      if (g_source_get_context (crawler_array->pdata[i]) == context)
	{
	  g_source_destroy (g_ptr_array_remove_index (crawler_array, i));
	  i--;
	}
    }
  G_UNLOCK (crawler_array_lock);
}
 
static gboolean
recurser_idle (gpointer data)
{
  GMainContext *context = data;
  gint i;
 
  for (i = 0; i < 10; i++)
    g_main_context_iteration (context, FALSE);
 
  return FALSE;
}
 
static gboolean
recurser_start (gpointer data)
{
  GMainContext *context;
  GSource *source;
 
  g_mutex_lock (context_array_mutex);
  context = context_array->pdata[g_random_int_range (0, context_array->len)];
  source = g_idle_source_new ();
  g_source_set_callback (source, recurser_idle, context, NULL);
  g_source_attach (source, context);
  g_source_unref (source);
  g_mutex_unlock (context_array_mutex);
 
  return TRUE;
}
 
int 
main (int argc, char *argv[])
{
  gint i;
 
  g_thread_init (NULL);
 
  context_array = g_ptr_array_new ();
  context_array_mutex = g_mutex_new ();
  context_array_cond = g_cond_new (); 
 
  crawler_array = g_ptr_array_new ();
 
  main_loop = g_main_loop_new (NULL, FALSE);
 
  for (i = 0; i < NTHREADS; i++)
    create_adder_thread ();
 
  /* Wait for all threads to start
   */
  g_mutex_lock (context_array_mutex);
 
  if (context_array->len < NTHREADS)
    g_cond_wait (context_array_cond, context_array_mutex);
 
  g_mutex_unlock (context_array_mutex);
 
  for (i = 0; i < NCRAWLERS; i++)
    create_crawler ();
 
  g_timeout_add (RECURSER_TIMEOUT, recurser_start, NULL);
 
  g_main_loop_run (main_loop);
  g_main_loop_unref (main_loop);
 
  return 0;
}


相關推薦

glib學習筆記——GLib核心應用支援The Main Event Loop

原文地址 描述 The main event loop manages all the available sources of events for GLib and GTK+ applications. These events can come from any n

Socket學習筆記常用基本函式

函式:u_long htonl(u_long hostlong)u_short htons(u_short hostshort)u_long ntohl(u_long netlong)u_short ntohs(u_short netshort)這上面四個函式類似,功能相似,都用來轉換資料格式。用

JavaSE 學習筆記封裝

延遲加載 分類 static str super 想要 oid 懶漢式 可靠性 封 裝(面向對象特征之一):是指隱藏對象的屬性和實現細節,僅對外提供公共訪問方式。 好處:將變化隔離;便於使用;提高重用性;安全性。 封裝原則:將不需要對外提供的內容都隱藏起來,把屬性都隱藏,提

JavaSE 學習筆記繼承

內容 訪問 類繼承 mil 抽象方法 ted 內部 -- 中一 繼 承(面向對象特征之一) 好處: 1:提高了代碼的復用性。 2:讓類與類之間產生了關系,提供了另一個特征多態的前提。 父類的由來:其實是由多個類不斷向上抽取共性內容而來的。 java中對於繼承,java只

學習筆記iptables

1.防火牆的基礎知識         首先需要認識到什麼是防火牆,防火牆是通過一些有順序的規則。給從網路中進入到主機應用層之間的通道上設定很多道攔截的口,每個口會有一堆規則去匹配。匹配上,如果是匹配結果是通過就放行,如果是匹配結果是拒絕,就不

Java學習筆記FreeTTS語音

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

AAC學習筆記Dagger

本文為《Android Architecture Components學習筆記》的一部分 文件程式碼為Kotlin,但是系統生成的程式碼仍然為Java 本人水平有限,如有不當之處請不吝賜教 Dagger初接觸 Dagger並不是AAC的一部分,但是在專案中

JavaWeb學習筆記XML2

文章目錄 xml的解析(jaxp) dom方式解析xml sax方式解析xml dom4j解析器 相關知識: https://blog.csdn.net/mokexfdgh/articl

JavaWeb學習筆記XML1

文章目錄 XML 表單提交方式 XML的介紹 XML的應用 XML的語法 XML的dtd約束 schema約束 相關知識: https://blog.csd

ReactiveCocoa 學習筆記RACEvent

RACEvent RACEvent 是 ReactiveCocoa 框架中用來表示訊號流所傳送的事件資訊的類,同三種訊號量相對應,該類可以分為三種。 typedef NS_ENUM(NSUInteger

TypeScript學習筆記 介面Interface

在java中,介面是用來定義一些規範,使用這些介面,就必須實現介面中的方法,而且介面中的屬性必須是常量。 javascript中是沒有介面的概念的。所以TypeScript在編譯成 JavaScrip

機器學習筆記SVMSVR演算法

學過SVM後,看了那麼多別人的文章,是時候自己總結一波了。權當寫的筆記供自己日後再回顧吧。 PS:結合自己在工作過程中(我這裡用SVR做股票預測)用到的知識來寫的,不會很全面,若有些知識這裡沒提及讀者

學習筆記JavaSE47--IO流9

public class Test91 { public static void main(String[] args) throws ClassNotFoundException { // 物件序列化 ObjectOutputStream oos = null; try { oos

學習筆記JavaSE40--IO流2

今天學習的內容是位元組流和位元組流緩衝區 位元組流與字元流的基本操作相同,注意位元組流的輸出沒有用到緩衝區,下例使用FileOutputStream類和FileInputStream類實現對文字檔案的讀寫(位元組流不能直接處理字元內容,只能直接處理位元組內容): pub

TensorFlow 莫煩視訊學習筆記例子

註釋連結 所有程式碼 # -*- coding: utf-8 -*- """ Created on Wed Apr 19 12:30:49 2017 @author: lg 同濟大學B406 """ import tensorflow as tf im

我的第一個spring boot程序spring boot 學習筆記

獲取json 了解 訪問 static 依賴 過程 public 獲取數據 gap 第一個spring boot程序 寫在前面:鑒於spring註解以及springMVC的配置有大量細節和知識點,在學習理解之後,我們將直接進入spring boot的學習,在後續學習中用到註

MyBatis學習筆記--關聯關係一對一和一對多

首先給大家推薦幾個網頁: http://blog.csdn.net/isea533/article/category/2092001 沒事看看 - MyBatis工具:www.mybatis.tk http://www.mybatis.org/mybatis-3/zh/gettin

java學習筆記webservice--WSDL文件及用myeclipse測試webservice

 >>接上篇 一、WSDL 定義:web services description language,用來描述web服務的xml格式的資訊。 標籤的解釋 1. <types>:定義了服務的namespace和關鍵資訊的型別(方法的引數型別和返回值的

學習hibernate出現錯誤--方言

pda data cells bird nbsp 版本問題 inno 提高 語言 最近在學習hibernate,其中關於錯誤的問題真是一頭大,各種各樣的奇葩錯誤層出不窮,簡直是受不了了。 用hibernate操作數據庫,在使用hibernate進行把持久

設計模式C++學習筆記十三Decorator裝飾模式

com img c++ 進行 done 設計 out set 筆記 裝飾模式,動態地給一個對象添加一些額外的職責。就增加功能來說,Decorator模式相比生成子類更為靈活。 13.1.解釋 main(),老爸 ISchoolReport,成績單接口 CFourthGrad