Glib学习(14) 线程 Threads

逃离我推掉我的手 2022-05-31 07:10 1509阅读 0赞

glib源码下载:http://ftp.gnome.org/pub/gnome/sources/glib/

glib帮助文档:https://developer.gnome.org/glib/

本节包括可移植支持线程,互斥锁,锁,条件和线程私有数据

描述

线程几乎像进程一样行事,但与进程不同,一个进程的所有线程共享相同的内存。 好处是它通过这个共享内存提供了相关线程之间的简单通信,但是缺点是如果程序没有经过精心的设计,奇怪的事情(所谓的“Heisenbugs”)可能会发生。 特别是,由于线程的并发特性,除非程序员通过同步原语明确强制执行,否则不会对在不同线程中运行的代码的执行顺序进行假设。
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没有线程,因此一些传统的UNIX API在线程程序中是有问题的。
一些值得注意的如下:
C库函数在静态分配的缓冲区(如strtok()或strerror())中返回数据。 对于其中的很多,有一些带有a_r后缀的线程安全变体,或者您可以查看相应的GLib API(如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()函数仅更改当前线程的语言环境。
daemon()函数以与上述不兼容的方式使用fork()。 它不应该与GLib程序一起使用。
GLib本身在内部完全是线程安全的(所有全局数据都被自动锁定),但是出于性能原因,单个数据结构实例不会自动锁定。 例如,您必须协调从多个线程访问相同的GHashTable。 这个规则的两个明显的例外是GMainLoop和GAsyncQueue,它们是线程安全的,不需要进一步的应用程序级锁定来从多个线程访问。 大多数refcounting函数(如g_object_ref())也是线程安全的。
GThreads的常见用法是将长时间运行的阻塞操作从主线程移出到工作线程。 对于GLib函数,比如单一的GIO操作,这不是必须的,并且使代码复杂化。 相反,函数的_async()版本应该从主线程中使用,而不需要在多个线程之间进行锁定和同步。 如果操作确实需要移动到工作线程,请考虑使用g_task_run_in_thread()或GThreadPool。 GThreadPool通常是比GThread更好的选择,因为它处理线程重用和任务排队; GTask在内部使用。
但是,如果需要按顺序执行多个阻塞操作,并且不能使用GTask,那么将它们移动到工作线程可以简化代码。

线程

GThreadFunc ()
指定传递给g_thread_new()或g_thread_try_new()的func函数的类型。

参数
data
数据传递给线程

返回
线程的返回值

g_thread_new ()
这个函数创建一个新的线程。 新的线程通过调用参数数据的func开始。 该线程将运行,直到func返回或直到从新线程调用g_thread_exit()。 func的返回值成为线程的返回值,可以通过g_thread_join()来获得。
该名称可以用于区分调试器中的线程。 它不用于其他目的,也不一定是唯一的。 一些系统将名称长度限制为16个字节。
如果线程无法创建,程序将中止。 如果你想尝试处理失败,请参阅g_thread_try_new()。
如果你使用线程来卸载(可能是很多)短期任务,GThreadPool可能比手动创建和跟踪多个GThread更合适。
要释放此函数返回的结构体,请使用g_thread_unref()。 请注意,g_thread_join()也隐式地取消了GThread。

参数
name
新线程的(可选)名称。[可空]
func
一个在新线程中执行的函数
data
提供给新线程的数据

返回
新的GThread

g_thread_try_new ()
这个函数与g_thread_new()相同,除了它允许失败的可能性。
如果线程无法创建(由于资源限制),则会设置错误并返回NULL。

参数
name
新线程的(可选)名称。[可空]
func
一个在新线程中执行的函数
data
提供给新线程的论据

返回
新的GThread,如果发生错误,则返回NULL

g_thread_ref ()
增加线程的引用计数。

参数
thread
GThread

返回
一个新的线程引用

g_thread_unref ()
减少线程的引用计数,可能释放与之关联的所有资源。
请注意,每个线程在运行时都会保存对GThread的引用,因此如果不再需要,可以放弃自己的引用。

参数
thread
GThread

g_thread_join ()
等待线程完成,即函数func,给g_thread_new(),返回或调用g_thread_exit()。 如果线程已经终止,那么g_thread_join()立即返回。
任何线程都可以通过调用g_thread_join()来等待任何其他线程,而不仅仅是它的“创建者”。 多个线程中调用g_thread_join()等待同一个线程会导致未定义的行为。
由func返回的值或给g_thread_exit()的值是由这个函数返回的。
g_thread_join()消耗对传入线程的引用。 这通常会导致GThread结构和相关的资源被释放。 使用g_thread_ref()来获得一个额外的引用,如果你想保持GThread超出g_thread_join()调用。

参数
thread
GThread

返回
线程的返回值

g_thread_yield ()
使调用线程自动放弃CPU,以便其他线程可以运行。
这个功能经常被用来作为一个方法使忙碌等待更少的占用。

g_thread_exit ()
终止当前线程。
如果另一个线程正在使用g_thread_join()等待我们,那么等待的线程将被唤醒并获得retval作为g_thread_join()的返回值。
使用参数retval调用g_thread_exit()等同于从函数func返回ret_val,如g_thread_new()所示。
您只能从您使用g_thread_new()或相关的API创建的线程中调用g_thread_exit()。 您不能从使用其他线程库或GThreadPool创建的线程中调用此函数。

参数
retval
这个线程的返回值

g_thread_self ()
这个函数返回对应于当前线程的GThread。 请注意,该函数不会增加返回结构的引用计数。
即使对于非GLib创建的线程(即由其他线程API创建的线程),该函数也会返回一个GThread。 这对于线程识别目的(即比较)可能是有用的,但是你不能在这些线程上使用GLib函数(比如g_thread_join())。

返回
表示当前线程的GThread

总结:glib实现了pthread基本功能,但是并没有完全涵盖pthread的函数,所以在某些时候可能会出现混用的情况。

例程如下:

  1. #include <glib.h>
  2. #include <glib/gprintf.h>
  3. #include <unistd.h>
  4. #include <sys/syscall.h>
  5. gpointer thread_func1 (gpointer data)
  6. {
  7. g_printf("%s %ld in\n", __func__, syscall(__NR_gettid));
  8. g_printf("%s %ld data is : %d\n", __func__, syscall(__NR_gettid), *((gint *)data));
  9. g_usleep (2000000);
  10. g_printf("%s %ld out\n", __func__, syscall(__NR_gettid));
  11. g_thread_exit ((gpointer)20);//等价于return
  12. //return (gpointer)20;
  13. }
  14. int main(int argc, char **argv)
  15. {
  16. g_printf ("main in\n");
  17. gint func1_data = 11;
  18. GThread *gthread = NULL;
  19. gthread = g_thread_new ("func1", thread_func1, &func1_data);
  20. //gthread = g_thread_try_new ("func1", thread_func1, &func1_data);
  21. //if(gthread == NULL) g_printf ("g_thread_try_new fail\n");
  22. g_printf ("g_thread_join %ld\n", (gint64)g_thread_join (gthread));
  23. g_usleep (5000000);
  24. g_printf ("main out\n");
  25. return 0;
  26. }

总结:锁的用法基本可linux标准锁一致,这里就翻译了,唯独特殊的是glib区分静态分配锁和动态分配锁。

例程如下:

  1. #include <glib.h>
  2. #include <glib/gprintf.h>
  3. #include <unistd.h>
  4. #include <sys/syscall.h>
  5. G_LOCK_DEFINE (thread_mutex);
  6. gint count = 0;
  7. gpointer thread_func1 (gpointer data)
  8. {
  9. gint tmp;
  10. while(1)
  11. {
  12. G_LOCK (thread_mutex);
  13. g_printf("%s %ld count: %d\n", __func__, syscall(__NR_gettid), count);
  14. tmp = count;
  15. count++;
  16. g_usleep (2000000);
  17. g_printf("%s %ld count++ tmp: %d count: %d\n", __func__, syscall(__NR_gettid), tmp, count);
  18. G_UNLOCK (thread_mutex);
  19. g_usleep (20000);
  20. }
  21. }
  22. gpointer thread_func2 (gpointer data)
  23. {
  24. gint tmp;
  25. while(1)
  26. {
  27. G_LOCK (thread_mutex);
  28. g_printf("%s %ld count: %d\n", __func__, syscall(__NR_gettid), count);
  29. tmp = count;
  30. count++;
  31. g_usleep (2000000);
  32. g_printf("%s %ld count++ tmp: %d count: %d\n", __func__, syscall(__NR_gettid), tmp, count);
  33. G_UNLOCK (thread_mutex);
  34. g_usleep (20000);
  35. }
  36. }
  37. int main(int argc, char **argv)
  38. {
  39. g_printf ("main in\n");
  40. GThread *gthread1 = NULL, *gthread2 = NULL;
  41. gthread1 = g_thread_new ("func1", thread_func1, NULL);
  42. gthread2 = g_thread_new ("func2", thread_func2, NULL);
  43. g_thread_join (gthread1);
  44. g_thread_join (gthread2);
  45. g_printf ("main out\n");
  46. return 0;
  47. }

线程条件

g_cond_init ()
初始化一个GCond,以便可以使用它。
这个函数对初始化已经被分配为更大结构的一部分的GCond很有用。 没有必要初始化静态分配的GCond。
要在不再需要GCond的情况下撤消g_cond_init()的效果,请使用g_cond_clear()。
在已经初始化的GCond上调用g_cond_init()会导致未定义的行为。

参数
cond
一个未初始化的GCond
从:2.32

g_cond_clear ()
用g_cond_init()释放分配给GCond的资源。
这个函数不应该与静态分配的GCond一起使用。
对线程阻塞的GCond调用g_cond_clear()会导致未定义的行为。

参数
cond
一个初始化的GCond
从:2.32

void g_cond_wait ()
原子级释放互斥锁并等待直到cond被发信号。 当这个函数返回时,互斥锁再次被锁定,并由调用线程拥有。
使用条件变量时,可能会发生虚假唤醒(即:即使未调用g_cond_signal(),也会返回g_cond_wait())。 被盗的唤醒也有可能发生。 这是当调用g_cond_signal()时,另一个线程在该线程之前获取互斥体,并以g_cond_wait()能够返回的方式修改程序的状态时,不再满足预期的条件。
为此,g_cond_wait()必须始终在循环中使用。 有关完整的示例,请参阅GCond的文档。

参数
cond
一个GCond
mutex
一个当前被锁定的GMutex

gboolean g_cond_timed_wait ()
g_cond_timed_wait自版本2.32开始已被弃用,不应在新编写的代码中使用。
改用g_cond_wait_until()。

gboolean g_cond_wait_until ()
等待直到任何cond被发信号或者end_time已经过去。
与g_cond_wait()一样,可能会发生虚假或被盗的唤醒。 出于这个原因,等待一个条件变量应该总是在一个循环中,基于一个显式检查的谓词。
如果条件变量被发送(或者在虚假唤醒的情况下),则返回TRUE。 如果end_time已过,则返回FALSE。
以下代码显示了如何在条件变量上正确执行定时等待(扩展了GCond文档中提供的示例):
gpointer
pop_data_timed (void)
{
gint64 end_time;
gpointer data;

g_mutex_lock (&data_mutex);

end_time = g_get_monotonic_time () + 5 * G_TIME_SPAN_SECOND;
while (!current_data)
if (!g_cond_wait_until (&data_cond, &data_mutex, end_time))
{
// timeout has passed.
g_mutex_unlock (&data_mutex);
return NULL;
}

// there is data for us
data = current_data;
current_data = NULL;

g_mutex_unlock (&data_mutex);

return data;
}
请注意,结束时间是在进入循环并重新使用之前计算的。 这是在这个API上使用绝对时间的动机 - 如果5秒钟的相对时间直接传递给调用并发生虚假唤醒,程序将不得不重新开始等待(这会导致等待时间超过5秒)。
参数
cond
一个GCond
mutex
一个当前被锁定的GMutex
end_time
单调的时间要等到

返回
信号为TRUE,超时为FALSE
从:2.32

void g_cond_signal ()
如果线程正在等待cond,至少其中一个被解锁。 如果没有线程在等待cond,这个函数不起作用。 虽然不需要,但在调用此函数的同时保持与等待线程相同的锁定是个好习惯。

参数
cond
一个GCond

void g_cond_broadcast ()
如果线程正在等待cond,它们全部被解除阻塞。 如果没有线程在等待cond,这个函数不起作用。 虽然不需要,但在调用此函数的同时锁定与等待线程相同的互斥锁是一个好习惯。

参数
cond
一个GCond

总结:其实这部分与linux也十分相似,基本功能相同,这里就不多说。

例程如下:

  1. #include <glib.h>
  2. #include <glib/gprintf.h>
  3. #include <unistd.h>
  4. #include <sys/syscall.h>
  5. GMutex *thread_mutex, *broad_mutex;
  6. GCond *cond1, *broad_cond;
  7. gint count = 0, broad_flag = 0;
  8. gpointer decrement (gpointer data)
  9. {
  10. g_printf("%s in\n", __func__);
  11. g_mutex_lock (thread_mutex);
  12. while (count == 0)
  13. g_cond_wait(cond1, thread_mutex);
  14. count--;
  15. g_printf("%s decrement:%d.\n", __func__, count);
  16. g_printf("out decrement.\n");
  17. g_mutex_unlock (thread_mutex);
  18. g_printf("%s out\n", __func__);
  19. return NULL;
  20. }
  21. gpointer increment (gpointer data)
  22. {
  23. g_printf("%s in\n", __func__);
  24. g_mutex_lock (thread_mutex);
  25. count++;
  26. g_printf("%s increment:%d.\n", __func__, count);
  27. if (count != 0)
  28. g_cond_signal (cond1);
  29. g_mutex_unlock (thread_mutex);
  30. g_printf("%s out\n", __func__);
  31. return NULL;
  32. }
  33. gpointer broadcast (gpointer data)
  34. {
  35. g_printf("%s in\n", __func__);
  36. g_usleep (5000000);
  37. g_mutex_lock (broad_mutex);
  38. //todo something
  39. broad_flag = 1;
  40. g_cond_broadcast (broad_cond);
  41. g_mutex_unlock (broad_mutex);
  42. g_printf("%s out\n", __func__);
  43. return NULL;
  44. }
  45. gpointer trigger1 (gpointer data)
  46. {
  47. g_printf("%s in\n", __func__);
  48. g_mutex_lock (broad_mutex);
  49. while (broad_flag == 0)
  50. g_cond_wait(broad_cond, broad_mutex);
  51. g_printf("%s recv broad_cond\n", __func__);
  52. g_mutex_unlock (broad_mutex);
  53. g_printf("%s out\n", __func__);
  54. }
  55. gpointer trigger2 (gpointer data)
  56. {
  57. g_printf("%s in\n", __func__);
  58. g_mutex_lock (broad_mutex);
  59. while (broad_flag == 0)
  60. g_cond_wait(broad_cond, broad_mutex);
  61. g_printf("%s recv broad_cond\n", __func__);
  62. g_mutex_unlock (broad_mutex);
  63. g_printf("%s out\n", __func__);
  64. }
  65. gpointer trigger3 (gpointer data)
  66. {
  67. g_printf("%s in\n", __func__);
  68. g_mutex_lock (broad_mutex);
  69. while (broad_flag == 0)
  70. g_cond_wait(broad_cond, broad_mutex);
  71. g_printf("%s recv broad_cond\n", __func__);
  72. g_mutex_unlock (broad_mutex);
  73. g_printf("%s out\n", __func__);
  74. }
  75. int main(int argc, char **argv)
  76. {
  77. g_printf ("main in\n");
  78. GThread *gthread1 = NULL, *gthread2 = NULL;
  79. cond1 = g_new(GCond, 1);
  80. g_cond_init (cond1);
  81. thread_mutex = g_new(GMutex, 1);
  82. g_mutex_init (thread_mutex);
  83. gthread1 = g_thread_new ("func1", decrement, NULL);
  84. g_usleep (2000000);
  85. gthread2 = g_thread_new ("func2", increment, NULL);
  86. g_thread_join (gthread1);
  87. g_thread_join (gthread2);
  88. g_mutex_clear (thread_mutex);
  89. g_cond_clear(cond1);
  90. g_printf ("----------broadcast cond test-----------\n");
  91. GThread *gthread3 = NULL, *gthread4 = NULL;
  92. broad_cond = g_new(GCond, 1);
  93. g_cond_init (broad_cond);
  94. broad_mutex = g_new(GMutex, 1);
  95. g_mutex_init (broad_mutex);
  96. gthread1 = g_thread_new ("broadcast", broadcast, NULL);
  97. gthread2 = g_thread_new ("trigger1", trigger1, NULL);
  98. gthread3 = g_thread_new ("trigger2", trigger2, NULL);
  99. gthread4 = g_thread_new ("trigger3", trigger3, NULL);
  100. g_thread_join (gthread1);
  101. g_thread_join (gthread2);
  102. g_thread_join (gthread3);
  103. g_thread_join (gthread4);
  104. g_mutex_clear (broad_mutex);
  105. g_cond_clear(broad_cond);
  106. g_printf ("main out\n");
  107. return 0;
  108. }

私有数据

这个功能linux也是支持的,只是极少被用到,因为这种功能总是可以使用其他方法实现。如果有兴趣的可以去参考linux的 pthread_key_create相关函数。

G_PRIVATE_INIT()
一个宏来协助一个GPrivate的静态初始化。
这个宏对于GDestroyNotify函数应该与键关联的情况非常有用。 当这个键被用来指向内存时,这个是需要的,当这个线程退出时这个内存应该被释放。
此外,当使用g_private_replace()时,GDestroyNotify也将被调用存储在键中的以前的值。
如果不需要GDestroyNotify,则不需要使用此宏 - 如果GPrivate在静态作用域中声明,那么默认情况下它将被正确初始化(即:全部为零)。 看下面的例子。
理解:在线程退出时,需要执行一些销毁动作时,需要使用G_PRIVATE_INIT()注册销毁函数。在线程退出时会自动将key作为参数执行注册的函数。

参数
notify
GDestroyNotify

g_private_get ()
返回线程局部变量键的当前值。
如果该线程中尚未设置该值,则返回NULL。 值不会在线程之间复制(例如,在创建新线程时)。

参数
key
一个GPrivate

返回
线程本地值

g_private_set ()
将线程局部变量键设置为在当前线程中具有值的值。
该函数与g_private_replace()的区别在于:GDestroyNotify不会被调用于旧的值。

参数
key
一个GPrivate
value
新的价值

g_private_replace ()
将线程局部变量键设置为在当前线程中具有值的值。
这个函数与g_private_set()的区别在于:如果前面的值是非NULL,那么对其运行GDestroyNotify处理程序。

参数
key
一个GPrivate
value
新的价值
从:2.32

例程如下:

  1. #include <glib.h>
  2. #include <glib/gprintf.h>
  3. #include <unistd.h>
  4. #include <sys/syscall.h>
  5. struct test_struct {
  6. gint i;
  7. gfloat k;
  8. };
  9. void destroy_notify(gpointer a)
  10. {
  11. g_printf ("destroy_notify in\n");
  12. //此处应该执行释放动态内存动作
  13. g_printf ("destroy_notify out\n");
  14. }
  15. static GPrivate private_key = G_PRIVATE_INIT (destroy_notify);
  16. gint count = 0;
  17. gpointer thread_func1 (gpointer data)
  18. {
  19. struct test_struct *struct_data = g_new(struct test_struct, 1);
  20. struct_data->i = 10;
  21. struct_data->k = 3.1415;
  22. g_private_set (&private_key, (gpointer)(struct_data));
  23. g_printf ("%s struct_data addr: 0x%p\n", __func__, struct_data);
  24. g_printf ("%s g_private_get(key) return addr: 0x%p\n", __func__, (struct test_struct *)g_private_get (&private_key));
  25. g_printf ("%s g_private_get(key) into struct_data.i: %d\nstruct_data.k: %f\n", __func__, ((struct test_struct *)g_private_get (&private_key))->i, ((struct test_struct *)g_private_get(&private_key))->k);
  26. g_usleep (2000000);
  27. }
  28. gpointer thread_func2 (gpointer data)
  29. {
  30. gint temp = 20;
  31. g_usleep (1000000);
  32. g_printf ("%s temp addr: 0x%p\n", __func__, &temp);
  33. g_private_set (&private_key, GINT_TO_POINTER (temp));
  34. g_printf ("%s g_private_get(key): 0x%p\n", __func__, (gint *)g_private_get(&private_key));
  35. g_printf ("%s g_private_get(key) temp: %d\n", __func__, GPOINTER_TO_INT (g_private_get(&private_key)));
  36. }
  37. int main(int argc, char **argv)
  38. {
  39. g_printf ("main in\n");
  40. GThread *gthread1 = NULL, *gthread2 = NULL;
  41. gthread1 = g_thread_new ("func1", thread_func1, NULL);
  42. gthread2 = g_thread_new ("func2", thread_func2, NULL);
  43. g_thread_join (gthread1);
  44. g_thread_join (gthread2);
  45. g_printf ("main out\n");
  46. return 0;
  47. }

一次性初始化

#define g_once()
具有给定的GOnce结构的进程对该例程的第一次调用将使用给定的参数调用func。 此后,使用相同的GOnce结构对g_once()的后续调用不会再次调用func,而是返回第一个调用的存储结果。 从g_once()返回时,一次的状态将是G_ONCE_STATUS_READY。
例如,一个互斥体或一个线程特定的数据键必须被创建一次。 在线程环境中,调用g_once()可确保初始化跨多个线程进行串行化。
在func中的同一个GOnce结构上递归地调用g_once()会导致死锁。
gpointer
get_debug_flags (void)
{
static GOnce my_once = G_ONCE_INIT;

g_once (&my_once, parse_debug_flags, NULL);

return my_once.retval;
}

参数
once
一个GOnce结构
func
GThreadFunc函数关联一次。 这个函数只被调用一次,而不管它和它相关的GOnce结构被传递给g_once()的次数。
arg
数据传递给func
从:2.4

gboolean g_once_init_enter ()
启动临界初始化部分时要调用的函数。 参数的位置必须指向一个静态的0初始化变量,它将在初始化部分的最后被设置为一个非零值。 结合g_once_init_leave()和唯一地址value_location,可以确保初始化部分在程序生命周期中只执行一次,并发线程被阻塞,直到初始化完成。 用于像这样的构造:
static gsize initialization_value = 0;

if (g_once_init_enter (&initialization_value))
{
gsize setup_value = 42; // initialization code here

  1. g\_once\_init\_leave (&initialization\_value, setup\_value);

}

// use initialization_value here
参数
location
包含0的静态可初始化变量的位置。[不可空]

返回
如果应该输入初始化部分,则为TRUE,否则为FALSE
从:2.14

void g_once_init_leave ()
与g_once_init_enter()相对应。 需要一个静态初始化变量0的初始化位置,以及初始化值不是0的位置。将该变量设置为初始化值,并释放g_once_init_enter()中对此初始化变量的并发线程阻塞。

参数
location
包含0的静态可初始化变量的位置。[不可空]
result
* value_location的新的非0值
从:2.14

整数指针的原子操作锁
void g_bit_lock ()
gboolean g_bit_trylock ()
void g_bit_unlock ()
void g_pointer_bit_lock ()
gboolean g_pointer_bit_trylock ()
void g_pointer_bit_unlock ()

guint g_get_num_processors ()
确定系统将为此过程同时安排的近似线程数。 这是为了用作g_thread_pool_new()用于CPU绑定任务和类似情况的参数。

总结:这部分linux也有函数,有兴趣的可以参考pthread_once相关函数。

发表评论

表情:
评论列表 (有 0 条评论,1509人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Thread线

    目录 需要笔记的可以关注私聊我发给你 Thread 线程的创建方式 方式一(继承Thread类方式) 方式二(实现Runnable方式) 方式三(实现Callab

    相关 线-Thread

    线程 线程和进程的区别: ①线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位。

    相关 【7.14/7.15】线Thread

    1):进程的就绪(Runnable)和阻塞(Blocked)的区别     当处理机空闲时,处于就绪状态的线程就会得到处理器资源,进入运行,当调用yield()时,处于运行状