一:线程私有数据:

线程是轻量级进程,进程在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. 朴素贝叶斯原理、实例与Python实现

    初步理解一下:对于一组输入,根据这个输入,输出有多种可能性,需要计算每一种输出的可能性,以可能性最大的那个输出作为这个输入对应的输出. 那么,如何来解决这个问题呢? 贝叶斯给出了另一个思路.根据历史记 ...

  2. Sql 小技巧(持续汇总)

    身份证号加密 select concat(left(`visitors`.`Card_id`,3),'****',right(`visitors`.`Card_id`,4)) AS `Card_id` ...

  3. 鸿蒙内核源码分析(Shell编辑篇) | 两个任务,三个阶段 | 百篇博客分析OpenHarmony源码 | v71.01

    子曰:"我非生而知之者,好古,敏以求之者也." <论语>:述而篇 百篇博客系列篇.本篇为: v71.xx 鸿蒙内核源码分析(Shell编辑篇) | 两个任务,三个阶段 ...

  4. P5048-[Ynoi2019 模拟赛]Yuno loves sqrt technology III【分块】

    正题 题目链接:https://www.luogu.com.cn/problem/P5048 题目大意 就是这个 [QA]区间众数,但空间很小 长度为\(n\)的序列,要求支持查找区间众数出现次数. ...

  5. P5180-[模板]支配树

    正题 题目链接:https://www.luogu.com.cn/problem/P5180 题目大意 给出\(n\)个点的一张有向图,求每个点支配的点数量. \(1\leq n\leq 2\time ...

  6. IE浏览器设置兼容性

    因为IE浏览器不兼容高版本的Jquery.Bootstrap等JS框架,导致页面在Google浏览器和在IE的显示完全不一样,所以需要对页面进行兼容性设置 <!--设置兼容性--> < ...

  7. position的五个不同的位置值

    一.position: static  无定位 HTML 元素默认情况下的定位方式为 static(静态). 静态定位的元素不受 top.bottom.left 和 right 属性的影响. posi ...

  8. Winform 空闲时间(鼠标键盘无操作)

    前言 Winform 在特定情况下,需要判断软件空闲时间(鼠标键盘无操作),然后在做一下一些操作. 实现 做了一个简单的例子,新建一个窗体,然后拖两个控件(Timer控件和label控件) using ...

  9. Dubbo与Zookeeper简单理解

    理论 在<分布式系统原理与范型>一书中有如下定义:"分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统 "; 分布式系统是由一组通过网络进行通信. ...

  10. 题解 Sue的小球/名次排序问题/方块消除/奥运物流

    Sue的小球 名次排序问题 方块消除 奥运物流 Sue的小球 题目大意 有 \(n\) 个小球在下落,初始位置 \((x_i,y_i)\),下落速度为 \(v_i\).你初始位置在 \(x_0\),速 ...