linux多线程学习笔记三---线程同步之信号量

时间:2022-02-19 15:14:48

一,共享数据

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

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

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

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

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

#include<pthread.h>
#include<stdio.h>
#include<semaphore.h>
#define NITERS 100000000
/*共享变量*/
unsigned int cnt = 0;
sem_t mutex;
void *count(void *arg)
{
int i;
for(i=0;i<NITERS;i++)
{
sem_wait(&mutex);
cnt++;
sem_post(&mutex);
}
return arg;
}
int main()
{
pthread_t tid1,tid2;
int status;
sem_init(&mutex,0,1);
status=pthread_create(&tid1,NULL,count,NULL);
if(status!=0)
{
printf("create error\n");
return 1;
}
status=pthread_create(&tid2,NULL,count,NULL);
if(status!=0)
{
printf("create error\n");
return 1;
}
status=pthread_join(tid1,NULL);
if(status!=0)
{
printf("join error\n");
return 1;
}
status=pthread_join(tid2,NULL);
if(status!=0)
{
printf("join error\n");
return 1;
}
if(cnt!=(unsigned)NITERS*2)
printf("Boom!,cnt=%d\n",cnt);
else
printf("Ok cnt=%d\n",cnt);
return O;
}
若没加信号量时运行结果为:Boom!,cnt=120924615  加上信号量后为:Ok cnt=200000000,通过信号量使线程互斥的执行cnt++。

三,利用信号量来调度资源共享资源
信号量的另一个重要作用是调度对共享资源的访问。对经典的生产者消费者模型,,生产者和消费者共享一个有n个槽的有界缓冲区,生产者把产品生产放入缓冲区中,消费者从缓冲区中取商品。因为插入和取出项目都包括更新共享变量,所有可以通过信号量实现互斥的访问。同时还要考虑当缓冲区满时,生产者必须等待,缓冲区空的时候消费者必须等待,这就涉及到资源的调度问题。

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<semaphore.h>
/* $begin sbuft */
typedef struct {
int *buf; /* Buffer array */
int n; /* Maximum number of slots */
int front; /* buf[(front+1)%n] is first item */
int rear; /* buf[rear%n] is last item */
sem_t mutex; /* Protects accesses to buf */
sem_t slots; /* Counts available slots */
sem_t items; /* Counts available items */
} sbuf_t;
/* $end sbuft */

void sbuf_init(sbuf_t *sp, int n);
void sbuf_deinit(sbuf_t *sp);
void sbuf_insert(sbuf_t *sp, int item);
int sbuf_remove(sbuf_t *sp);
void *producer_thread(void *arg);
void *consumer_thread(void *arg);
sbuf_t sbuf;
int main()
{
pthread_t producer_id,consumer_id;
sbuf_init(&sbuf,10);
pthread_create(&producer_id,NULL,producer_thread,NULL);
pthread_create(&consumer_id,NULL,consumer_thread,NULL);
pthread_join(producer_id,NULL);
pthread_join(consumer_id,NULL);
//sbuf_deinit(&sbuf);
return 0;
}
/* Create an empty, bounded, shared FIFO buffer with n slots */
/* $begin sbuf_init */
void sbuf_init(sbuf_t *sp, int n)
{
sp->buf = (int*)malloc(n* sizeof(int));
sp->n = n; /* Buffer holds max of n items */
sp->front = sp->rear = 0; /* Empty buffer iff front == rear */
sem_init(&sp->mutex, 0, 1); /* Binary semaphore for locking */
sem_init(&sp->slots, 0, n); /* Initially, buf has n empty slots */
sem_init(&sp->items, 0, 0); /* Initially, buf has zero data items */
}
/* $end sbuf_init */

/* Clean up buffer sp */
/* $begin sbuf_deinit */
void sbuf_deinit(sbuf_t *sp)
{
free(sp->buf);
}
/* $end sbuf_deinit */

/* Insert item onto the rear of shared buffer sp */
/* $begin sbuf_insert */
void sbuf_insert(sbuf_t *sp, int item)
{
sem_wait(&sp->slots); /* Wait for available slot */
sem_wait(&sp->mutex); /* Lock the buffer */
sp->buf[(++sp->rear)%(sp->n)] = item; /* Insert the item */
printf("insert item %d\n",item);
sem_post(&sp->mutex); /* Unlock the buffer */
sem_post(&sp->items); /* Announce available item */
}
/* $end sbuf_insert */

/* Remove and return the first item from buffer sp */
/* $begin sbuf_remove */
int sbuf_remove(sbuf_t *sp)
{
int item;
sem_wait(&sp->items); /* Wait for available item */
sem_wait(&sp->mutex); /* Lock the buffer */
item = sp->buf[(++sp->front)%(sp->n)]; /* Remove the item */
printf("remove item %d\n",item);
sem_post(&sp->mutex); /* Unlock the buffer */
sem_post(&sp->slots); /* Announce available slot */
return item;
}
/* $end sbuf_remove */
void* producer_thread(void *arg)
{
int i=0;
for(;i<20;i++)
{
sbuf_insert(&sbuf,i);
//printf("insert item %d\n",i);
}
return NULL;
}
void* consumer_thread(void *arg)
{
int i=0,item;
for(;i<20;i++)
{
item=sbuf_remove(&sbuf);

}
return NULL;
}