一:线程私有数据:

线程是轻量级进程,进程在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. 自制Chrome扩展插件:用于重定向js

    前言 作为一个前端开发, 在调试生产环境的代码时,是否苦于生产环境代码被压缩,没有sourcemap? 有没有想过将生产环境的js直接重定向为本地开发环境的js? 玩微前端时,有没有想过用本地的子应用 ...

  2. docker启动jenikns,提示 :This image is for research only, DO NOT USE

    下载的jenkins镜像有问题?

  3. JDBC封装的工具类

    1. JDBC封装的工具类 public class JDBCUtil { private static Properties p = new Properties(); private static ...

  4. whistle安装

    可参考官方帮助文档:https://wproxy.org/whistle/install.html 系统:windows10   jdk:1.8.0_171    node:10.16.0    np ...

  5. 感恩笔记之SQL语句操纵数据集基本功能模板

    SQL查询_基本功能 一 SQL语句整体架构 SELECT --1 查询数据表 INTO --2 新建数据表 FROM --3 查询数据表 WHERE --4 筛选数据表 ORDER BY --5 排 ...

  6. Blazor Webassembly多标签页开发

    最近准备用Blazor Webassembly做后台开发要用到多标签页,找了半天发现绝大多数都是Blazor Server的多标签没有Webassembly.没办法只能自己想办法造轮子了. 查了许多资 ...

  7. 从零入门 Serverless | 一文详解 Serverless 技术选型

    作者 | 李国强 阿里云资深产品专家 今天来讲,在 Serverless 这个大领域中,不只有函数计算这一种产品形态和应用类型,而是面向不同的用户群体和使用习惯,都有其各自适用的 Serverless ...

  8. WEB 标准以及 W3C 的理解与认识

    01.  WEB标准 ①  web标准 简单来说可以分为结构.表现和行为. ②  结构:主要是有HTML标签组成(通俗点说,在页面body里面我们写入的标签都是为了页面的结构)   表现:即指css样 ...

  9. NOIP模拟77

    前言 感觉最近太飘了,这次考试是挺好的一次打击(好像也不算是). 犯了一个智障错误(双向边一倍数组 100pts->30pts)别的就.. T1 最大或 解题思路 一开始我以为是一个找规律,然而 ...

  10. 洛谷4172 WC2006水管局长(LCT维护最小生成树)

    这个题和魔法森林感觉有很相近的地方啊 同样也是维护一个类似最大边权最小的生成树 但是不同的是,这个题是有\(cut\)和询问,两种操作.... 这可如何是好啊? 我们不妨倒着来考虑,假设所有要\(cu ...