Day35、线程同步、生产者和消费者、信号量级

一、            线程和线程的同步

pthread_cancel(3)

#include<pthread.h>

intpthread_cancel(pthread_t thread);

Compile and linkwith -pthread.

功能:给一个线程发送终止的请求

参数:指定的线程id

返回值:0成功   错误返回错误码

线程的终止:有三种

return    pthread_exit   pthread_cancel 终止的信息是-1

pthread_detach(3)

#include<pthread.h>

intpthread_detach(pthread_t thread);

Compile and linkwith -pthread.

功能:标记一个线程为detached状态,当这个状态的线程终止的时候,线程的资源会被自动回收。(和join不能公用)

参数:thread:线程的id

返回值:0成功   错误返回错误码

 

举例

  1 #include<stdio.h>

  2 #include<pthread.h>

  3

  4 void *th_fn1(void *arg){

  5     printf("thread1 returning...\n");

  6    return (void *)1;

  7 }

  8 void *th_fn2(void *arg){

  9    printf("thread2 exit...\n");

 10    pthread_exit((void *)2);

 11 }

 12 void *th_fn3(void *arg){

 13    while(1){

 14        printf("thread3 exit...\n");

 15        sleep(1);

 16     }

 17 }

 18

 19 int main(void){

 20    pthread_t tid;

 21    void *ret;

 22    pthread_create(&tid,NULL,th_fn1,NULL);

 23    pthread_join(tid,&ret);

24     printf("thread1 exit code%d\n",(int)ret);

 25

 26    pthread_create(&tid,NULL,th_fn2,NULL);

 27    pthread_join(tid,&ret);

 28    printf("thread2 exit code %d\n",(int)ret);

 29

 30    pthread_create(&tid,NULL,th_fn3,NULL);

 31    pthread_cancel(tid);

 32    pthread_join(tid,&ret);

 33    printf("thread3 exit code %d\n",(int)ret);

 34

 35    pthread_create(&tid,NULL,th_fn1,NULL);

 36    pthread_detach(tid);

 37    return 0;

 38 }

tarena@tarena-virtual-machine:~/day35$./join

thread1returning...

thread1 exitcode 1

thread2 exit...

thread2 exitcode 2

thread3 exit...

thread3 exitcode -1

 

  1 #include<stdio.h>

  2 #include<stdlib.h>

  3 #include<pthread.h>

  4 #define NLOOP 5

  5 int counter=0;

  6 void *doit(void *);

  7 int main(void){

  8    int i=0;

  9    pthread_t tidA, tidB;

 10    //创建两个线程

 11    pthread_create(&tidA,NULL,&doit,NULL);

 12    pthread_create(&tidB,NULL,&doit,NULL);

 13    //等着线程的结束,以便回收资源

 14  //  pthread_join(tidA,NULL);   (测试)

 15  //  pthread_join(tidB,NULL);  (测试)  子线程相当于调用一个函数

 16    printf("i=%d",i);

 17    return 0;

 18 }

 19 void *doit(void *arg){

 20    int i,val;

 21    for(i=0;i<NLOOP;i++){

 22        val=counter;

 23        printf("%x:%d\n",(unsigned int)pthread_self(),val+1);

24         counter=val+1;

 25     }

 26    return NULL;

 27 }

线程的同步:

同步的实现:

互斥锁:mutex锁

pthread_mutex_init(3)

需要对变量初始化和销毁

pthread_mutex_init(pthread_mutex_t *__mutex, __const pthread_mutexattr_t *__mutexattr)

pthread_mutex_destroy(pthread_mutex_t *__mutex)

功能:销毁Mutex指定的锁

参数:mutex 变量

pthread_mutex_trylock(pthread_mutex_t *__mutex)

尝试加锁

pthread_mutex_lock(pthread_mutex_t *__mutex)

 

举例:lock.c  两个线程进行同步,

  1 #include<stdio.h>

  2 #include<stdlib.h>

  3 #include<pthread.h>

  4 #define NLOOP 50

  5 pthread_mutex_tc_mutex=PTHREAD_MUTEX_INITIALIZER;

  6 int counter=0;

  7 void *doit(void *);

  8 int main(void){

  9    pthread_t tidA, tidB;

 10    //创建两个线程

 11    pthread_create(&tidA,NULL,&doit,NULL);

 12    pthread_create(&tidB,NULL,&doit,NULL);

 13    //等着线程的结束,以便回收资源

 14    pthread_join(tidA,NULL);

 15    pthread_join(tidB,NULL);

 16    return 0;

 17 }

 18 void *doit(void *arg){

 19    int i,val;

 20    static int counter;

 21    for(i=0;i<NLOOP;i++){

 22        pthread_mutex_lock(&c_mutex);

 23        counter++;

24         printf("%x:%d\n",(unsignedint)pthread_self(),counter);

 25        pthread_mutex_unlock(&c_mutex);

 26     }

 27    return NULL;

 28 }

tarena@tarena-virtual-machine:~/day35$gcc lock.c -lpthread -o lock

tarena@tarena-virtual-machine:~/day35$./lock

b6d9ab40:1

b6d9ab40:2

b6d9ab40:3

b6d9ab40:4

b6d9ab40:5

b6d9ab40:6

b6d9ab40:7

b6d9ab40:8

….

….

b759bb40:98

b759bb40:99

b759bb40:100

 

如果不加锁可能会异步

22       // pthread_mutex_lock(&c_mutex);

23         counter++;

24         printf("%x:%d\n",(unsignedint)pthread_self(),counter);

25      //  pthread_mutex_unlock(&c_mutex);

b6df1b40:1

b6df1b40:3

b6df1b40:4

b6df1b40:5

b6df1b40:6

….

b6df1b40:101

b75f2b40:2

b75f2b40:102

 

条件变量:(conditionbariable)

线程需要等待某个条件成立才能继续往下执行,条件不成立,线程就阻塞。等待另外一个线程将条件设置成成立,此时唤醒阻塞的线程,继续往下执行

pthread_cond_t类型的变量来表示条件变量

pthread_cond_init(3)

#include<pthread.h>

pthread_cond_tcond=PTHREAD_COND_INITIALIZER;

pthread_cond_init(pthread_cond_t *cond,  pthread_condattr_t *cond_attr)

功能:初始化这个条件变量

参数:

Cond:条件变量的地址

Cond_attr:NULL

返回值:

 

Int  pthread_cond_destroy (pthread_cond_t *cond);

功能:销毁条件变量

参数:cond:条件变量的地址

intpthread_cond_signal (pthread_cond_t *cond);

功能:唤醒在条件变量上等待的某个线程

int pthread_cond_broadcast(pthread_cond_t *cond)

功能:唤醒在条件变量上等待的全部线程

intpthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex);

功能:等待条件成立

第一步:解锁mutex

第二步:阻塞等待

第三步:当被唤醒的时候,重新获得mutex锁

参数:cond条件变量

Mutex:mutex锁变量

intpthread_cond_timedwait (pthread_cond_t *cond, pthread_mutex_t *mutex,,const struct timespec *abstime)

功能:有时限的等待

参数:abstime 规定了等待的时限

 

演示一个生产者和消费者的例子。生产者生产一个结构体串在链表的头部,消费者从头部取走结构体

举例:pro-con.c

  1 #include<stdio.h>

  2 #include<stdlib.h>

  3 #include<pthread.h>

  4 typedef struct msg{

  5    struct msg *next;

  6    int num;

  7 }msg_t;

  8 msg_t *head;

  9 //初始化锁和条件变量

 10 pthread_cond_th_prod=PTHREAD_COND_INITIALIZER;

 11 pthread_mutex_tlock=PTHREAD_MUTEX_INITIALIZER;

 12 //生产者往链表的头部插入结点

 13 void *producer(void *arg){

 14    msg_t *mp;

 15    for(;;){

 16        mp=(msg_t *)malloc(sizeof(msg_t));

 17        mp->num=rand()%1000+1;

 18        printf("producer %d\n",mp->num);

 19        //需要往链表里插入数据

 20        pthread_mutex_lock(&lock);

 21        mp->next=head;

 22        head=mp;

 23        //数据插入结束

24         pthread_mutex_unlock(&lock);

 25        pthread_cond_signal(&h_prod);

 26        sleep(rand()%5);

 27     }

 28 }

 29 //消费者从链表的头部取走结点

 30 void *consumer(void *arg){

 31    msg_t *mp;

 32    for(;;){

 33        //先获得锁

 34        pthread_mutex_lock(&lock);

 35        //链表中没有结点的时候

 36        while(head==NULL){

 37            pthread_cond_wait(&h_prod,&lock);

 38        }

 39        mp=head;

 40        head=head->next;//释放头结点

 41        //解锁

 42        pthread_mutex_unlock(&lock);

 43        printf("consumer %d\n",mp->num);

 44        free(mp);

 45        mp=NULL;

 46        sleep(rand()%5);

 47     }

 48 }

 49 int main(void){

 50    pthread_t tidp,tidc;

 51    //创建两个线程

 52    pthread_create(&tidp,NULL,producer,NULL);

 53    pthread_create(&tidp,NULL,consumer,NULL);

 54    //等待线程的结束

 55    pthread_join(tidp,NULL);

 56    pthread_join(tidc,NULL);

 57    return 0;

 58 }

producer 457

producer 43

consumer 43

consumer 457

producer 920

consumer 920

信号量:

既可以进程间通信,也可以线程间通信

mutex 非0即1

semaphore和mutex类似,表示可用资源的数量

使用sem_t类型表示信号量

 

sem_init(3)

Link with -lrt or -pthread.

#include <semaphore.h>

int sem_init(sem_t *sem, int pshared, unsigned int value);

功能:初始化一个未命名的信号量

参数:

sem:要初始化的信号量

value:用value的值初始化信号量

pshared:0 用于多线程

返回值:

0表示成功。

-1表示失败,errno被设置

 

sem_wait(3)

#include <semaphore.h>

int sem_wait(sem_t *sem);

功能:使信号量-1,如果信号量大于0,则立即返回。若信号量等于1,阻塞等待。

参数:sem:指定的信号量

返回值:0表示成功。

-1表示失败,errno被设置

 

sem_destroy(3)

#include <semaphore.h>

int sem_destroy(sem_t *sem);

功能:销毁信号量

参数:sem:要销毁的信号量

返回值:0代表成功,-1代表失败,errno被设置

 

sem_trywait(3)

int sem_trywait(sem_t *sem);

非阻塞,其它同sem_wait

 

sem_post(3)

int sem_post(sem_t *sem);

功能:使信号量加1,在这个信号量上等待的线程被唤醒

参数:sem:指定的信号量

返回值:0代表成功,-1代表失败,errno被设置

 

实现生产者和消费者的代码,基于固定大小的环形队列

生产和消费队列共5个,当生产的数字大于消费的数字大于等于5时,等待消费,直到消费时,才开始生产

vi sem_cilcle.c

  1 #include<stdio.h>

  2 #include<semaphore.h>

  3 #include<pthread.h>

  4 #define NUM 5

  5 int queue[NUM];//环形队列

  6 sem_t b_number,p_number;

  7 //生产者

  8 void *producer(void *arg){

  9    int in=0;

 10    while(1){

 11        sem_wait(&b_number);

 12        queue[in]=rand()%1000+1;

 13        printf("producer %d\n",queue[in]);

 14        sem_post(&p_number);

 15        in=(in+1)%NUM;

 16        sleep(rand()%2);

 17     }

 18 }

 19 //消费者

 20 void *consumer(void *arg){

 21    int c=0;

 22    while(1){

 23        sem_wait(&p_number);

24         printf("consumer%d\n",queue[c]);

 25        queue[c]=0;

 26        sem_post(&b_number);

 27        c=(c+1)%NUM;

 28        sleep(rand()%8);

 29     }

 30 }

 31 int main(void){

 32    pthread_t pid,cid;

 33    //初始化信号量

 34    sem_init(&b_number,0,NUM);

 35    sem_init(&p_number,0,0);

 36    //创建两个线程

 37    pthread_create(&cid,NULL,consumer,NULL);

 38    pthread_create(&pid,NULL,producer,NULL);

 39    //等待线程的结束

 40    pthread_join(cid,NULL);

 41    pthread_join(pid,NULL);

 42    //销毁信号量

 43    sem_destroy(&b_number);

 44    sem_destroy(&p_number);

 45    return 0;

 46 }

tarena@tarena-virtual-machine:~/day35$gcc sem_circle.c -lpthread -o sem

tarena@tarena-virtual-machine:~/day35$./sem

producer 384

producer 778

consumer 384

producer 336

producer 493

producer 85

consumer 414

consumer 981

 

二、            信号量集semaphore

消息队列

共享内存

信号量:

信号量与其它几种IPC机制(管道、共享内存和消息队列)都不一样,它的目的不是在进程之间搭建数据流通的桥梁,而是提供一个可为多个进程共同访问计数器,实时跟踪可用资源的数量,以解决多个用户分享有限资源时的竞争与冲突问题

ftok(2)

semget(2)

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/sem.h>

int semget(key_tkey, int nsems, int semflg);

功能:获取一个跟key值相关的信号量的id

参数:

key:ftok(2)产生的键值

nsems:要创建的信号集包含的信号的个数

semflg:IPC_CREAT

返回值:

-1失败  返回信号量集的id

信号量的操作:

semop(2)

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/sem.h>

int semop(intsemid, struct sembuf *sops, unsigned nsops);

功能:信号量集的操作

参数:

semid:semget(2)的返回值,在这个信号量集上执行操作

sops:指向了进行操作的结构体的数组

noops:要操作的信号的个数

struct sops{

       unsigned short sem_num;  /* semaphore number */

           short          sem_op;   /* semaphore operation */

           short          sem_flg;  /* operation flags */

}

sem_num:信号量在信号量集中的下标索引

sem_op:每个信号量的操作类型

sem_op>0:给信号量加上sem_op值

sem_op=0:如果设置了IPC_NOWAIT,马上返回,错误设置成EAGAIN,没有设置。进程进入睡眠状态,直到信号量为0

sem_op<0:给信号量加上sem_op值,表示进程控制资源

sem_flg:IPC_NOWAIT或者IPC_SEM

返回值:

0成功 -1失败,errno被设置

semctl(2)

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/sem.h>

int semctl(intsemid, int semnum, int cmd, ...);

参数:

semid:

semnum:在集合中的下标

cmd:指明控制操作的类型

union semun {

               int    val;   /* Value for SETVAL */

               struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */

               unsigned short  *array; /* Array for GETALL, SETALL */

               struct seminfo  *__buf; /* Buffer for IPC_INFO

};

举例:server.c

      Client

重点:内存的管理

进程的管理

线程的管理

信号管理

文件管理

文件系统管理

网络通讯

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值