C语言线程对象和线程存储的实现

当每个线程为各自的变量使用全局标识符时,为保留这些变量各自的数据,可以采用线程对象(thread-local object)和线程存储(thread-specific storage)。

这两项技术允许在一个给定线程中执行的函数可以共享数据而不造成冲突,即便当其他线程也在执行同样函数的情况下。

使用线程对象

线程对象是在声明中包含新存储类修饰符 _Thread_local 的全局或静态对象。这意味着:每一个线程拥有属于自己的线程对象实例,它在线程启动时创建并初始化。对象的存储周期等于线程的运行时间。在一个线程内表达式里面的线程对象名,将引用这个对象在当前线程下的本地实例。

修饰符 _Thread_local 可以与修饰符 static 或 extern 同时使用。头文件 threads.h 定义了 thread_local 作为 _Thread_local 的同义词。在例 1 中,主线程和新启动线程各自拥有线程本地变量 var 的一个实例。

【例1】使用一个线程对象

#include <stdio.h>
#include <threads.h>
thread_local int var = 10;
void print_var(void){ printf("var = %d\n", var); }
int func(void *);        // 线程函数
int main(int argc, char *argv[])
{
  thrd_t th1;
  if ( thrd_create( &th1, func, NULL ) != thrd_success ){
   fprintf(stderr,"Error creating thread.\n"); return 0xff;
  }
  print_var();        // 输出:var = 10
  thrd_join(th1, NULL);
  return 0;
}
int func(void *arg)       // 线程函数
{
  var += 10;         // 线程本地变量
  print_var();        // 输出:var = 20
  return 0
}

使用线程存储

线程存储技术要比线程对象更加灵活。例如,独立线程可以使用不同大小的内存。它们可以动态地分配内存,并通过调用析构函数再次释放内存。同时,可以使用相同的标识符访问这些独立线程所在的不同内存区域。

这种灵活性通过初始创建一个全局的键(key)实现,该键表示了一个指向线程存储的指针。然后,独立线程通过指定其线程存储的位置加载这个指针。该全局键值是类型为 tss_t 的对象。头文件 threads.h 包含了该类型的定义以及 4 个用于管理线程存储(简称 TSS)函数的声明:

int tss_create(tss_t*key,tss_dtor_t dtor);

通过析构函数 dtor 生成一个新的 TSS 指针,并且将 key 引用的对象设置为唯一标识该 TSS 指针的值。类型 tss_dtor_t 是一个函数指针,定义为 void(*)(void*)(它指的是一个函数指针,该函数参数为 void 指针,并且该函数没有返回值)。dtor 的返回值可以是一个空指针。

void tss_delete(tss_t key);

释放 TSS 键 key 所使用的所有资源。

int tss_set(tss_t key,void*val);

对于调用 tss_set()的线程,将 key 所标识的 TSS 指针设置为 val 所引用的内存地址。

void*tss_get(tss_t key);

返回指向内存块的指针,该内存块为正在调用的线程通过函数 tss_set()设置。如果发生错误,tss_get()返回 NULL。

如果函数 tss_create()和 tss_set()发生错误,则返回 thrd_error;否则,返回 thrd_success。

例 2 中的程序在动态分配的线程存储中,保留线程的名称。

【例2】使用线程存储

#include <threads.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
tss_t key;               // 用于TSS指针的全局键
int thFunc(void *arg);     // 线程函数
void destructor(void *data);  // 析构函数
int main(void)
{
  thrd_t th1, th2;
  int result1 = 0, result2 = 0;
  // 创建一个TSS密钥
  if (tss_create(&key, destructor) != thrd_success)
   return -1;
  // 创建线程
  if (thrd_create(&th1, thFunc, "Thread_1") != thrd_success
     || thrd_create(&th2, thFunc, "Thread_2") != thrd_success)
    return -2;
  thrd_join(th1, &result1); thrd_join(th2, &result2);
  if ( result1 != 0 || result2 != 0 )
    fputs("Thread error\n", stderr);
  else
    puts("Threads finished without error.");
  tss_delete(key);      // 释放TSS指针所有的资源
  return 0;
}
void print(void)        // 显示线程存储
{
 printf( "print: %s\n", (char*)tss_get(key) );
}
int thFunc( void *arg )
{
  char *name = (char*)arg;
  size_t size = strlen(name)+1;
  // 设置线程存储
  if ( tss_set(key, malloc(size)) != thrd_success )
   return -1;
  // 存储数据
  strcpy((char*)tss_get(key), name);
  print();
  return 0;
}
void destructor(void *data)
{
 printf("Destructor for %s\n", (char*)data);
 free(data);      // 释放内存
}

到此这篇关于C语言线程对象和线程存储的实现的文章就介绍到这了,更多相关C语言线程对象和线程存储内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C语言创建线程thread_create()的方法

    在头文件 threads.h 中,定义和声明了支持多线程的宏.类型和函数.所有直接与线程相关的标识符,均以前缀 thrd_ 作为开头.例如,thrd_t 是一个对象类型,它标识了一个线程. 函数 thrd_create()用于创建并开始执行一个新线程.函数 thrd_create()的其中一个参数为在新线程中需要被执行的函数 thrd_create()的其中一个参数为在新线程中需要被执行的函数.thrd_create()的完整原型是: int thrd_create(thrd_t *thr, t

  • C语言实现支持动态拓展和销毁的线程池

    本文实例介绍了C 语言实现线程池,支持动态拓展和销毁,分享给大家供大家参考,具体内容如下 实现功能 1.初始化指定个数的线程 2.使用链表来管理任务队列 3.支持拓展动态线程 4.如果闲置线程过多,动态销毁部分线程 #include <stdio.h> #include <pthread.h> #include <stdlib.h> #include <signal.h> /*线程的任务队列由,函数和参数组成,任务由链表来进行管理*/ typedef str

  • C语言实现用户态线程库案例

    轮子年年有人造,我们也来凑热闹,参考协程实现,大概有以下几种方法: 1)利用setjmp,longjmp 2)利用ucontext接口函数 3)汇编 (线程无非就是多了个抢占功能,由定时器触发,而非自愿让出运行权限) 因为我写的时候还没看到其他帖子,如果看到了,铁定会用最直观的ucontext接口写的(注意,在macOSX中已经标注为废除,头文件得换做sys/ucontext.h),结果就是我用了汇编来写,但是尽量不用汇编来写整个switch_to调度函数(这样有个明显的坏处,那就是用gas/n

  • C语言实现多线程定时器实例讲解

    1. 大致功能介绍 实现任务列表,定时器会间隔一段时间遍历列表发现要执行的任务 任务列表中的所有任务并行执行 每个任务都可以有自己的定时器,并且可以选择是否要重复执行 定义方便的任务函数实现接口 定时器可以由用户自定义何时启动和停止 提供等待功能,保证任务列表中的所有任务执行完成 提供任务列表的传参功能 2. API库介绍 void setTick(int val); 设置定时间的间隔时间tick,若设置tick为1000,且任务的定时器时间为1000,则任务会在1秒后执行,默认tick为1秒,

  • c语言线程终止练习示例

    复制代码 代码如下: #include <stdio.h>#include <stdlib.h>#include <pthread.h> void *t1(void *args) { return (void *) 0;} void *t2(void *args) { printf("thread 2 param[args] = %d\n", args); pthread_exit((void *) 3);} void *t3(void *args)

  • c语言多线程编程使用示例

    复制代码 代码如下: #include <stdio.h>#include <stdlib.h>#include <pthread.h> #define THREAD_NUM 10 void *test(void *args) { printf("tid %d: i say 'Hello'.\n", args); return NULL;} int main() { int i, err; pthread_t child[THREAD_NUM]; f

  • c语言实现多线程动画程序示例

    该程序是利用opengl图形库与fmod音频库写的一个简单3d动画程序.该程序在vs下运行良好,若缺少相关dll文件请确认已配制fmod与opengl库. mixmodel.cpp 复制代码 代码如下: // mixmodel.cpp : 定义控制台应用程序的入口点.// #include "stdafx.h" //定义一个线程DWORD WINAPI SoundProc( LPVOID LPVIDEOPARAMETERS);//光照变量GLfloat  whiteLight[] =

  • C语言如何正确的终止正在运行的子线程

    最近开发一些东西,线程数非常之多,当用户输入Ctrl+C的情形下,默认的信号处理会把程序退出,这时有可能会有很多线程的资源没有得到很好的释放,造成了内存泄露等等诸如此类的问题,本文就是围绕着这么一个使用场景讨论如何正确的终止正在运行的子线程.其实本文更确切的说是解决如何从待终止线程外部安全的终止正在运行的线程 首先我们来看一下,让当前正在运行的子线程停止的所有方法 1.任何一个线程调用exit 2.pthread_exit 3.pthread_kill 4.pthread_cancel 下面我们

  • C语言线程对象和线程存储的实现

    当每个线程为各自的变量使用全局标识符时,为保留这些变量各自的数据,可以采用线程对象(thread-local object)和线程存储(thread-specific storage). 这两项技术允许在一个给定线程中执行的函数可以共享数据而不造成冲突,即便当其他线程也在执行同样函数的情况下. 使用线程对象 线程对象是在声明中包含新存储类修饰符 _Thread_local 的全局或静态对象.这意味着:每一个线程拥有属于自己的线程对象实例,它在线程启动时创建并初始化.对象的存储周期等于线程的运行时

  • 深入解析Java的线程同步以及线程间通信

    Java线程同步 当两个或两个以上的线程需要共享资源,它们需要某种方法来确定资源在某一刻仅被一个线程占用.达到此目的的过程叫做同步(synchronization).像你所看到的,Java为此提供了独特的,语言水平上的支持. 同步的关键是管程(也叫信号量semaphore)的概念.管程是一个互斥独占锁定的对象,或称互斥体(mutex).在给定的时间,仅有一个线程可以获得管程.当一个线程需要锁定,它必须进入管程.所有其他的试图进入已经锁定的管程的线程必须挂起直到第一个线程退出管程.这些其他的线程被

  • Android性能优化之线程监控与线程统一详解

    目录 背景 常规解决方案 线程监控 当前线程统计 线程信息具体化 线程统一 Thread创建 注意 总结 背景 在我们日常开发中,多线程管理一直是非常头疼的问题之一,尤其在历史性长,结构复杂的app中,线程数会达到好几百个甚至更多,然而过多的线程不仅仅带来了内存上的消耗同时也降低了cpu调度的效率,过多的cpu调度带来的消耗的坏处甚至超过了多线程带来的好处. 在我们日常开发中,通常会遇到以下几个问题 某个场景会创造过多的线程,最终导致oom 线程池过多问题,比如三方库有一套线程池,自己项目也有一

  • Java concurrency线程池之线程池原理(一)_动力节点Java学院整理

    ThreadPoolExecutor简介 ThreadPoolExecutor是线程池类.对于线程池,可以通俗的将它理解为"存放一定数量线程的一个线程集合.线程池允许若个线程同时允许,允许同时运行的线程数量就是线程池的容量:当添加的到线程池中的线程超过它的容量时,会有一部分线程阻塞等待.线程池会通过相应的调度策略和拒绝策略,对添加到线程池中的线程进行管理." ThreadPoolExecutor数据结构 ThreadPoolExecutor的数据结构如下图所示: 各个数据在Thread

  • Java concurrency线程池之线程池原理(二)_动力节点Java学院整理

    线程池示例 在分析线程池之前,先看一个简单的线程池示例. import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; public class ThreadPoolDemo1 { public static void main(String[] args) { // 创建一个可重用固定线程数的线程池 ExecutorService pool = Executors.newFixedThre

  • Android中子线程和UI线程通信详解

    Android中子线程和UI线程之间通信的详细解释 1.在多线程编程这块,我们经常要使用Handler,Thread和Runnable这三个类,那么他们之间的关系你是否弄清楚了呢?下面详解一下. 2.首先在开发Android应用时必须遵守单线程模型的原则: Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行. 3.Handler: (1).概念: Handler是沟通Activity 与Thread/runnable的桥梁.而Handler是运行在主UI线程中的,它与子线程

  • 浅谈java中守护线程与用户线程

    Java线程分为两类分别为daemon线程(守护线程)和User线程(用户线程),在JVM启动时候会调用main函数,main函数所在的线程是一个用户线程,这个是我们可以看到的线程,其实JVM内部同时还启动了好多守护线程,比如垃圾回收线程.那么守护线程和用户线程有什么区别那?区别之一是当最后一个非守护线程结束时候,JVM会正常退出,而不管当前是否有守护线程,也就是说守护线程是否结束并不影响JVM的退出.言外之意是只要有一个用户线程还没结束正常情况下JVM就不会退出. 那么Java中如何创建一个守

  • Python多线程Threading、子线程与守护线程实例详解

    本文实例讲述了Python多线程Threading.子线程与守护线程.分享给大家供大家参考,具体如下: 线程与进程: 线程对于进程来说,就好似工厂里的工人,分配资源是分配到工厂,工人再去处理. 线程是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源. 在单个程序中同时运行多个线程完成不同的工作,称为多线程 对于IO密集型的程序来说,多线程可以利用读IO的时间去做其他事[IO并不占用CPU,这就好像A

  • 谈谈Java中的守护线程与普通线程

    守护线程与普通线程的唯一区别是:当JVM中所有的线程都是守护线程的时候,JVM就可以退出了:如果还有一个或以上的非守护线程则不会退出.(以上是针对正常退出,调用System.exit则必定会退出) 所以setDeamon(true)的唯一意义就是告诉JVM不需要等待它退出,让JVM喜欢什么退出就退出吧,不用管它. 守护线程在没有用户线程可服务时自动离开,在Java中比较特殊的线程是被称为守护(Daemon)线程的低级别线程.这个线程具有最低的优先级,用于为系统中的其它对象和线程提供服务.将一个用

随机推荐