Chinaunix首页 | 论坛 | 博客
  • 博客访问: 305656
  • 博文数量: 85
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 800
  • 用 户 组: 普通用户
  • 注册时间: 2014-10-18 15:21
文章分类

全部博文(85)

文章存档

2017年(1)

2016年(19)

2015年(55)

2014年(10)

我的朋友

分类: LINUX

2015-03-22 17:51:40

一,共享数据

一组并发线程运行在一个进程的上下文中,每个线程都有它自己独立的线程上下文,包括线程ID、栈、栈指针、程序计数器、条件代码和通用寄存器。每个 线程和其他线程一起共享进程上下文的剩余部分。包括整个用户虚拟地址空间,它是由只读文本、读写数据、堆以及所有的共享库代码和数据区域组成的。线程也共 享同样的打开文件的集合。所以任何线程都可以访问共享虚拟存储器的任意位置。如果某个线程修改了一个存储器位置,那么其他每个线程最终都能在它读这个位置 时发现这个变化。

多线程的C程序中的变量根据他们的存储类型被映射到虚拟存储器。

全局变量:全局变量定义在外的变量。在运行函数之时,虚拟存储器的读/写区域只包含每个全 局变量的一个实例。本地自动变量:本地自动变量就是定义在函数内部但没有static属性的变量。在运行时,每个线程的栈都包含它自己的所有本地自动变量 的实例。即使多个线程执行同一个线程程。因为线程拥有自己的栈空间。本地静态变量:本地静态变量是定义在函数内部并有static属性的变量。和全局变量 一样,虚拟存储器的读/写区域只包含在程序中声明的每个本地静态变量的一个实例。由此可知全局变量和本地静态变量都属于共享变量。

二,用信号量同步线程

共享变量十分方便,同时也引入同步错误的可能性。,可以通过信号量来同步线程。Posix信号量的三个基本操作时sem_init,sem_wait和sem_post,学过操作系统的都知道PV操作,其中的sem_wait相当于P操作,sem_post相当于V操作.


点击(此处)折叠或打开

  1. #include<semaphore.h>
  2.     int sem_init(sem_t *sem,0,unsigned int value);
  3.     int sem_wait(sem_t*s);
  4.     int sem_post(sem_t*s);
  5.     返回:若成功为0,若出错则为-1
看如下代码:

点击(此处)折叠或打开

  1.     #include<pthread.h>
  2.     #include<stdio.h>
  3.     #include<semaphore.h>
  4.     #define NITERS 100000000
  5.     /*共享变量*/
  6.     unsigned int cnt = 0;
  7.     sem_t mutex;
  8.     void *count(void *arg)
  9.     {
  10.         int i;
  11.         for(i=0;i<NITERS;i++)
  12.         {
  13.             sem_wait(&mutex);
  14.             cnt++;
  15.             sem_post(&mutex);
  16.         }
  17.         return arg;
  18.     }
  19.     int main()
  20.     {
  21.         pthread_t tid1,tid2;
  22.         int status;
  23.         sem_init(&mutex,0,1);
  24.         status=pthread_create(&tid1,NULL,count,NULL);
  25.         if(status!=0)
  26.         {
  27.             printf("create error\n");
  28.             return 1;
  29.         }
  30.         status=pthread_create(&tid2,NULL,count,NULL);
  31.         if(status!=0)
  32.         {
  33.             printf("create error\n");
  34.             return 1;
  35.         }
  36.         status=pthread_join(tid1,NULL);
  37.         if(status!=0)
  38.         {
  39.             printf("join error\n");
  40.             return 1;
  41.         }
  42.         status=pthread_join(tid2,NULL);
  43.         if(status!=0)
  44.         {
  45.             printf("join error\n");
  46.             return 1;
  47.         }
  48.         if(cnt!=(unsigned)NITERS*2)
  49.             printf("Boom!,cnt=%d\n",cnt);
  50.         else
  51.             printf("Ok cnt=%d\n",cnt);
  52.         return O;
  53.     }
若没加信号量时运行结果为:Boom!,cnt=120924615  加上信号量后为:Ok cnt=200000000,通过信号量使线程互斥的执行cnt++。

叫其信号量,其实也可用信号灯来理解
下面就是从这个实体名词出发,解释了信号量机制

三. 信号灯 
信号灯与互斥锁和条件变量的主要不同在于"灯"的概念,灯亮则意味着资源可用,灯灭则意味着不可用。如果说后两中同步方式侧重于"等待"操作,即 资源不可用的话,信号灯机制则侧重于点灯,即告知资源可用;没有等待线程的解锁或激发条件都是没有意义的,而没有等待灯亮的线程的点灯操作则有效,且能保 持灯亮状态。当然,这样的操作原语也意味着更多的开销。 

信号灯的应用除了灯亮/灯灭这种二元灯以外,也可以采用大于1的灯数,以表示资源数大于1,这时可以称之为多元灯。 

1. 创建和注销 
POSIX信号灯标准定义了有名信号灯和无名信号灯两种,但LinuxThreads的实现仅有无名灯,同时有名灯除了总是可用于多进程之间以外,在使用上与无名灯并没有很大的区别,因此下面仅就无名灯进行讨论。 

int sem_init(sem_t *sem, int pshared, unsigned int value) 
这是创建信号灯的API,其中value为信号灯的初值,pshared表示是否为多进程共享而不仅仅是用于一个进程。LinuxThreads 没有实现多进程共享信号灯,因此所有非0值的pshared输入都将使sem_init()返回-1,且置errno为ENOSYS。初始化好的信号灯由 sem变量表征,用于以下点灯、灭灯操作。 

int sem_destroy(sem_t * sem) 
被注销的信号灯sem要求已没有线程在等待该信号灯,否则返回-1,且置errno为EBUSY。除此之外,LinuxThreads的信号灯注销函数不做其他动作。 

2. 点灯和灭灯 
int sem_post(sem_t * sem) 
点灯操作将信号灯值原子地加1,表示增加一个可访问的资源。 

int sem_wait(sem_t * sem) 
int sem_trywait(sem_t * sem) 
sem_wait()为等待灯亮操作,等待灯亮(信号灯值大于0),然后将信号灯原子地减1,并返回。sem_trywait()为sem_wait()的非阻塞版,如果信号灯计数大于0,则原子地减1并返回0,否则立即返回-1,errno置为EAGAIN。 

3. 获取灯值 
int sem_getvalue(sem_t * sem, int * sval) 

读取sem中的灯计数,存于*sval中,并返回0。 

4. 其他 
sem_wait()被实现为取消点,而且在支持原子"比较且交换"指令的体系结构上,sem_post()是唯一能用于异步信号处理函数的POSIX异步信号安全的API。
阅读(980) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~