一:线程私有数据:

线程是轻量级进程,进程在fork()之后,子进程不继承父进程的锁和警告,别的基本上都会继承,而vfork()与fork()不同的地方在于vfork()之后的进程会共享父进程的地址空间,但是有了写实复制(fork()之后的子进程也不会直接复制父进程的地址空间在另一块内存,除非子进程修改了某种信息,才会在内存中为它重新复制一块区域)的出现之后,vfork()基本上就没什么作用了。但是线程与进程相比,还是有许多优点,比如节约资源,它复制的东西比vfork()之后的进程复制的东西还要少;并且多线程共享数据空间,所以通信就容易;还共享地址空间,所以线程之间的切换就快。线程的私有数据是在与其他线程共享进程数据的背景下,自己所拥有的信息,有以下几点:

  • 线程号(无符号长整形(unsigned long))
  • 一组寄存器的值(与cpu之间数据交换最快,线程会在运行的时候得到内核分配的寄存器,时间片完之后会把结果写入内存,寄存器会被回收。)
  • 堆栈
  • 错误处理
  • 信号屏蔽字
  • errno变量
  • 调度优先级
  • 私有的存储空间
  • 私有数据(TSD,一键多值技术,接下来我们重点看看)

这个私有数据还挺多的,下面我们主要看看私有数据的最后一条,TSD,一键多值技术。首先我们解释一下为什么要用它:

虽然线程之间共享数据空间为程序带来了便利,但是我们线程内部也会有这种需求:假设我们线程1内部有3个函数,而这三个函数需要对同一个变量 i 操作,那么这个变量 i 就可以看作是线程1内部自己的“全局变量“,但是我们如果在主线程中定义 i 为全局变量,而另一个线程2中也需要定义和 i 一样的变量(并且i在线程1和线程2之中的意义也相同),那么这个主线程中的全局变量 i 就能被两个线程访问,这样一来,线程1访问到的 i 可能刚好被线程2修改过,就会产生写无法预知的结果。(举个例字:errno变量,我们每个线程都拥有errno变量,但是每个线程的errno变量都不同,而且对于某一个线程内部,errno变量就是“全局“的,线程中的任何函数都可以修改它)

为了避免上述问题,我们真正的在主线程中引入了这样一个全局变量,就是我们的”key”——键,即一个键对应多个数值,每个线程都可以去访问这个键,只是不同的线程访问到的是属于自己的不同与别的线程的数据。一键多值技术靠的是一个关键的数据结构,即TSD池,也就是TSD池为我们提供了全局变量”key”。下面我们看一段代码(代码中包含了4个常用函数)

pthread_key_t  key;                          //申请一个key
void *func2()
{
int tsd = 2;
pthread_setspecific(key,(void*)&tsd); //将key与pthread2的变量tsd相关联
printf("i am thread2 :%u,my tsd:%d\n",pthread_self(),tsd);//输出pthread2的变量
pthread_exit(0);
} void *func1()
{
int tsd = 0;
int *status2;
pthread_t tid2; pthread_setspecific(key,(void*)&tsd); //将key与pthread1的变量tsd相关联
printf("i am thread1 :%u,my tsd:%d\n",pthread_self(),tsd);//输出pthread1的变量
pthread_create(&tid2,NULL,func2,NULL);
pthread_join(tid2,(void**)&status2);
pthread_exit(0); }
int main(int argc,char *argv[])
{
pthread_t tid1,tid2;
int *status;
pthread_key_create(&key,NULL); //创建一个key pthread_create(&tid1,NULL,func1,NULL);
pthread_join(tid1,(void**)&status);
pthread_key_delete(key); //删除key exit(0);
} 输出结果如下:
[kiosk@yangbodong 20150804]$ ./a.out
i am thread1 :2131724032,my tsd:0
i am thread2 :2123331328,my tsd:2
虽然thread1和thread2都关联的是同一个key,变量都是tsd,但是它们访问到的值是不同的。

二:互斥锁和条件变量。
线程最大的特点就是资源的共享性,但是同步问题又是资源共享的一个难点,所以就有了互斥锁和条件变量。
1:互斥锁

下面谈谈我对互斥锁的理解:首先它是锁,而且它是用来锁别人的,也就是当你访问某个文件的时候,你可以把别人锁在文件外边,不让他访问,当你访问完成后,解锁,这时,锁还在文件那里,下一个线程谁过去首先抢到锁,就可以把别人锁到外边,自己访问,这样可以解决多线程对一个文件同时访问时出错。

下面我们看一段代码(代码中包含了3个常用函数)

pthread_mutex_t  number_mutex;                      //定义一把锁
int globvar = 1; //定义一个全局变量globvar = 1 void *func1()
{
pthread_mutex_lock(&number_mutex); //加锁函数
globvar++;
printf("thread1---globvar:%d\n",globvar);
pthread_mutex_unlock(&number_mutex); //解锁函数
pthread_exit(0);
}
void *func2()
{
pthread_mutex_lock(&number_mutex);
globvar++;
printf("thread2---globvar:%d\n",globvar);
pthread_mutex_unlock(&number_mutex);
pthread_exit(0);
}
void *func3()
{
pthread_mutex_lock(&number_mutex);
globvar++;
printf("thread3---globvar:%d\n",globvar);
pthread_mutex_unlock(&number_mutex);
pthread_exit(0);
} int main(int argc,char *argv[])
{
pthread_t tid1,tid2,tid3;
void *status1;
void *status2;
void *status3; pthread_mutex_init(&number_mutex,NULL); //对锁进行初始化
pthread_create(&tid1,NULL,func1,NULL);
pthread_create(&tid2,NULL,func2,NULL);
pthread_create(&tid3,NULL,func3,NULL);
pthread_join(tid1,(void**)&status1);
pthread_join(tid2,(void**)&status2);
pthread_join(tid3,(void**)&status3);
exit(0); }
下面是运行结果:
[kiosk@yangbodong 20150804]$ ./a.out
thread1---globvar:2
thread2---globvar:3
thread3---globvar:4
[kiosk@yangbodong 20150804]$ ./a.out
thread2---globvar:2
thread1---globvar:3
thread3---globvar:4
注意:这两次运行结果不仅说明了加锁保证了某一时刻只有一个线程去访问globvar,而且说明了他们线程抢到锁的顺序也是随机的,不确定的。

2:条件变量

条件变量是利用线程间共享全局变量的一种机制,它在宏观上就相当于if语句,满足条件执行,不满足,等待条件成立。主要包含两个动作:一个动作是等待条件变为真,另一个是执行后设置条件为真,为了保证条件变量可以被准确的修改,互斥锁就扮演了重要角色,我对全局变量的修改加锁,当我的锁被释放的时候,我让等待条件变为真,这时候就可以保证修改的准确性。

下面我们看一段代码:

pthread_mutex_t  mutex;
pthread_cond_t cond;
int var = 0;
void *func2()
{
while(var < 10)
{
printf("thread2 is runninig\n");
pthread_mutex_lock(&mutex);
if(var%2 != 0)
pthread_cond_wait(&cond,&mutex);//如果是奇数就让它阻塞
printf("pthread 2 var :%d\n",var); //如果是偶数,就让它输出
pthread_mutex_unlock(&mutex);
sleep(1);
}
} void *func1()
{
pthread_cleanup_push(pthread_mutex_unlock,&mutex);
for(var = 0;var <= 10;var++)
{ printf("thread1 is running\n");
pthread_mutex_lock(&mutex);
if(var%2 == 0) //如果是偶数,就发出信号,唤醒上面的wait,但是wait能不能
pthread_cond_signal(&cond); //收到信号与它是没有关系的,它就会继续往下执行
else
printf("pthread 1 var :%d\n",var);
pthread_mutex_unlock(&mutex);
sleep(1); }
pthread_cleanup_pop(0);
} int main(int argc,char *argv[])
{
pthread_t tid1,tid2;
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&cond,NULL);
pthread_create(&tid1,NULL,func1,NULL);
pthread_create(&tid2,NULL,func2,NULL); sleep(10);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
}
下面是运行结果:
thread2 is runninig
pthread 2 var :0
thread1 is running
thread2 is runninig
thread1 is running
pthread 1 var :1
thread1 is running
pthread 2 var :2
thread1 is running
pthread 1 var :3
thread2 is runninig
thread1 is running
pthread 2 var :4
thread1 is running
pthread 1 var :5
thread2 is runninig
thread1 is running
pthread 2 var :6
thread1 is running
pthread 1 var :7
thread2 is runninig
thread1 is running
pthread 2 var :8
thread1 is running
pthread 1 var :9
thread2 is runninig
从结果我们可以验证,如果调用pthread_cond_wait()函数,我们的此线程就会阻塞在那里,等待一个signal将它激活,当然,这只是激活一个线程,如果要有多个,我们还可以使用pthread_cond_broadcast函数。

线程私有数据TSD——一键多值技术,线程同步中的互斥锁和条件变量的更多相关文章

  1. ZT linux 线程私有数据之 一键多值技术

    这个原作者的这个地方写错了 且他举的例子非常不好.最后有我的修正版本 pthread_setspecific(key, (void *)&my_errno); linux 线程私有数据之一键多 ...

  2. linux 线程的同步 二 (互斥锁和条件变量)

    互斥锁和条件变量 为了允许在线程或进程之间共享数据,同步时必须的,互斥锁和条件变量是同步的基本组成部分. 1.互斥锁 互斥锁是用来保护临界区资源,实际上保护的是临界区中被操纵的数据,互斥锁通常用于保护 ...

  3. linux线程私有数据---TSD池

    进程内的所有线程共享进程的数据空间,所以全局变量为所有线程共有.在某些场景下,线程需要保存自己的私有数据,这时可以创建线程私有数据(Thread-specific Data)TSD来解决.在线程内部, ...

  4. 进程间通信机制(管道、信号、共享内存/信号量/消息队列)、线程间通信机制(互斥锁、条件变量、posix匿名信号量)

    注:本分类下文章大多整理自<深入分析linux内核源代码>一书,另有参考其他一些资料如<linux内核完全剖析>.<linux c 编程一站式学习>等,只是为了更好 ...

  5. linux c 线程间同步(通信)的几种方法--互斥锁,条件变量,信号量,读写锁

    Linux下提供了多种方式来处理线程同步,最常用的是互斥锁.条件变量.信号量和读写锁. 下面是思维导图:  一.互斥锁(mutex)  锁机制是同一时刻只允许一个线程执行一个关键部分的代码. 1 . ...

  6. node源码详解(七) —— 文件异步io、线程池【互斥锁、条件变量、管道、事件对象】

    本作品采用知识共享署名 4.0 国际许可协议进行许可.转载保留声明头部与原文链接https://luzeshu.com/blog/nodesource7 本博客同步在https://cnodejs.o ...

  7. 非常精简的Linux线程池实现(一)——使用互斥锁和条件变量

    线程池的含义跟它的名字一样,就是一个由许多线程组成的池子. 有了线程池,在程序中使用多线程变得简单.我们不用再自己去操心线程的创建.撤销.管理问题,有什么要消耗大量CPU时间的任务通通直接扔到线程池里 ...

  8. Linux 线程同步的三种方法(互斥锁、条件变量、信号量)

    互斥锁 #include <cstdio> #include <cstdlib> #include <unistd.h> #include <pthread. ...

  9. Linux进程间通信IPC学习笔记之同步一(线程、互斥锁和条件变量)

    基础知识: 测试代码: 参考资料: Posix 多线程程序设计

随机推荐

  1. Feign超时不生效问题

    使用Feign作为RPC调用组件,可以配置连接超时和读取超时两个参数 使用Feign配置超时需要注意:Feign内部使用了负载均衡组件Ribbon,而Ribbon本身也有连接超时和读取超时相关配置一. ...

  2. Hadoop的高可用搭建

    在已经安装完hadoop单机和zookeeper前提下 1.免密钥 ssh-keygen -t rsa 分发秘钥 ssh-copy-id -i master ssh-copy-id -i node1 ...

  3. postgres 基础SQL语句 增删改

    查看已创建的数据库:select datname from pg_database; 查看所有数据库的详细信息:select * from pg_database 创建数据库:create datab ...

  4. python 文件夹扫描

    扫描指定文件夹下的文件.或者匹配指定后缀和前缀的函数. 假设要扫描指定文件夹下的文件,包含子文件夹,调用scan_files("/export/home/test/") 假设要扫描 ...

  5. cordova 打包 守护进程无法启动

    方案 1 : 添加环境变量 _JAVA_OPTIONS = -Xmx512M 2: 在新建的 系统变量里 变量名   _JAVA_OPTIONS 变量值   -Djava.net.preferIPv4 ...

  6. Spring Cloud Gateway 没有链路信息,我 TM 人傻了(下)

    本系列是 我TM人傻了 系列第五期[捂脸],往期精彩回顾: 升级到Spring 5.3.x之后,GC次数急剧增加,我TM人傻了 这个大表走索引字段查询的 SQL 怎么就成全扫描了,我TM人傻了 获取异 ...

  7. 鸿蒙源码分析系列(总目录) | 百万汉字注解 百篇博客分析 | 深入挖透OpenHarmony源码 | v8.23

    百篇博客系列篇.本篇为: v08.xx 鸿蒙内核源码分析(总目录) | 百万汉字注解 百篇博客分析 | 51.c.h .o 百篇博客.往期回顾 在给OpenHarmony内核源码加注过程中,整理出以下 ...

  8. P3760-[TJOI2017]异或和【树状数组】

    正题 题目链接:https://www.luogu.com.cn/problem/P3760 题目大意 给出\(n\)个数字的一个序列\(a\),求它所有区间和的异或和 \(n\leq 10^5,\s ...

  9. canvas 实现简单的画板功能 1.0

    canvas 实现自由画线,变换颜色.画笔大小,撤销上一步等简单功能 <!DOCTYPE html> <html lang="en"> <head&g ...

  10. 使用率激增250%,这份报告再将 Serverless 推向幕前

    ​ 作者 | 望宸 来源 | Serverless 公众号 相比去年,国外 Serverless 的适用群体在迅速扩大,函数执行时长不断增加,使用方式也越加成熟,开发者工具也更加开放.本文是对 Dat ...