网站备案中商城服务性质是什么,域名申请的流程,千瓜数据,网站开发者选项文章目录1.条件变量1.1 条件变量函数#xff1a;1.2 pthread_cond_init 函数1.3 pthread_cond_destroy 函数1.4 pthread_cond_wait 函数1.5 pthread_cond_timedwait 函数1.6 pthread_cond_signal 函数1.7 pthread_cond_broadcast 函数2.生产者消费者模型3.条件变量的优点…
文章目录1.条件变量1.1 条件变量函数1.2 pthread_cond_init 函数1.3 pthread_cond_destroy 函数1.4 pthread_cond_wait 函数1.5 pthread_cond_timedwait 函数1.6 pthread_cond_signal 函数1.7 pthread_cond_broadcast 函数2.生产者消费者模型3.条件变量的优点4.信号量5.信号量函数5.1 信号量基本操作5.2 sem_init 函数5.3 sem_destroy 函数5.4 sem_wait 函数5.5 sem_post 函数5.6 sem_trywait 函数5.7 sem_timedwait 函数6.信号量举例7.生产者消费者信号量模型8.哲学家吃饭问题9.共享内存1.条件变量
条件变量本身不是锁但它也可以造成线程阻塞。通常与互斥锁配合使用。给多线程提供一个会合的场所。1.1 条件变量函数
pthread_cond_init 函数
pthread_cond_destroy 函数
pthread_cond_wait 函数
pthread_cond_timedwait 函数
pthread_cond_signal 函数
pthread_cond_broadcast 函数
以上 6 个函数的返回值都是成功返回 0 失败直接返回错误号。
pthread_cond_t 类型 用于定义条件变量
pthread_cond_t cond;1.2 pthread_cond_init 函数 初始化一个条件变量 int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);参 2attr 表条件变量属性通常为默认值传 NULL 即可
也可以使用静态初始化的方法初始化条件变量
pthread_cond_t cond PTHREAD_COND_INITIALIZER;1.3 pthread_cond_destroy 函数 销毁一个条件变量 int pthread_cond_destroy(pthread_cond_t *cond);1.4 pthread_cond_wait 函数 阻塞等待一个条件变量 int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);函数作用
阻塞等待条件变量 cond参 1满足释放已掌握的互斥锁解锁互斥量相当于 pthread_mutex_unlock(mutex) ,1.2.两步为一个原子操作。当被唤醒pthread_cond_wait 函数返回时解除阻塞并重新申请获取互斥锁 pthread_mutex_lock(mutex);
1.5 pthread_cond_timedwait 函数 限时等待一个条件变量 int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);参 3 参看 man sem_timedwait 函数查看 struct timespec 结构体。struct timespec {time_t tv_sec; /* seconds */ 秒long tv_nsec; /* nanosecondes*/ 纳秒
}形参 abstime绝对时间。
如time(NULL)返回的就是绝对时间。而 alarm(1)是相对时间相对当前时间定时 1 秒钟。struct timespec t {1, 0};
pthread_cond_timedwait (cond, mutex, t); 只能定时到 1970 年 1 月 1 日 00:00:01 秒(早已经过去)正确用法
time_t cur time(NULL); 获取当前时间。
struct timespec t; 定义 timespec 结构体变量 t
t.tv_sec cur1; 定时 1 秒
pthread_cond_timedwait (cond, mutex, t); 传参 参 APUE.11.6 线程同步条件变量小节
在讲解 setitimer 函数时我们还提到另外一种时间类型struct timeval {time_t tv_sec; /* seconds */ 秒suseconds_t tv_usec; /* microseconds */ 微秒};1.6 pthread_cond_signal 函数 唤醒至少一个阻塞在条件变量上的线程 int pthread_cond_signal(pthread_cond_t *cond);1.7 pthread_cond_broadcast 函数 唤醒全部阻塞在条件变量上的线程 int pthread_cond_broadcast(pthread_cond_t *cond);2.生产者消费者模型
线程同步典型的案例即为生产者消费者模型而借助条件变量来实现这一模型是比较常见的一种方法。
假定有两个线程一个模拟生产者行为一个模拟消费者行为。
两个线程同时操作一个共享资源一般称之为汇聚生产向其中添加产品消费者从中消费掉产品。#include stdlib.h
#include unistd.h
#include pthread.h
struct msg {struct msg *next;int num;
};
struct msg *head;
pthread_cond_t has_product PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock PTHREAD_MUTEX_INITIALIZER;void *consumer(void *p)
{struct msg *mp;for (;;) {pthread_mutex_lock(lock);while (head NULL) { //头指针为空,说明没有节点 可以为 if 吗pthread_cond_wait(has_product, lock);}mp head; head mp-next; //模拟消费掉一个产品pthread_mutex_unlock(lock);printf(-Consume ---%d\n, mp-num);free(mp);sleep(rand() % 5);}
}void *producer(void *p)
{struct msg *mp;while (1) {mp malloc(sizeof(struct msg));mp-num rand() % 1000 1; //模拟生产一个产品printf(-Produce ---%d\n, mp-num);pthread_mutex_lock(lock);mp-next head;head mp;pthread_mutex_unlock(lock);pthread_cond_signal(has_product); //将等待在该条件变量上的一个线程唤醒sleep(rand() % 5);}
}int main(int argc, char *argv[])
{pthread_t pid, cid;srand(time(NULL));pthread_create(pid, NULL, producer, NULL);pthread_create(cid, NULL, consumer, NULL);pthread_join(pid, NULL);pthread_join(cid, NULL);return 0;
}zhaoxrzhaoxr-ThinkPad-E450:~/pthread$ ./pthread_cond
-Produce ---425
-Consume ---425
-Produce ---486
-Consume ---486
-Produce ---699
-Consume ---699
-Produce ---736
-Consume ---736
-Produce ---452
-Consume ---452
-Produce ---529
-Consume ---529
-Produce ---664
-Consume ---664
-Produce ---340
-Consume ---340
^C3.条件变量的优点
相较于 mutex 而言条件变量可以减少竞争。
如直接使用 mutex除了生产者、消费者之间要竞争互斥量以外
消费者之间也需要竞争互斥量但如果汇聚链表中没有数据消费者之间竞争互斥锁是无意义的。
有了条件变量机制以后只有生产者完成生产才会引起消费者之间的竞争。提高了程序效率。4.信号量
进化版的互斥锁1 -- N
由于互斥锁的粒度比较大如果我们希望在多个线程间对某一对象的部分数据进行共享
使用互斥锁是没有办法实现的只能将整个数据对象锁住。
这样虽然达到了多线程操作共享数据时保证数据正确性的目的却无形中导致线程的并发性下降。
线程从并行执行变成了串行执行。与直接使用单进程无异。
信号量是相对折中的一种处理方式既能保证同步数据不混乱又能提高线程并发。5.信号量函数
sem_init 函数
sem_destroy 函数
sem_wait 函数
sem_trywait 函数
sem_timedwait 函数
sem_post 函数
以上 6 个函数的返回值都是成功返回 0 失败返回-1同时设置 errno。(注意它们没有 pthread 前缀)
sem_t 类型本质仍是结构体。但应用期间可简单看作为整数忽略实现细节类似于使用文件描述符。
sem_t sem; 规定信号量 sem 不能 0。头文件 semaphore.h5.1 信号量基本操作
sem_wait: 1. 信号量大于 0则信号量-- 类比 pthread_mutex_lock2. 信号量等于 0造成线程阻塞sem_post 将信号量同时唤醒阻塞在信号量上的线程 类比 pthread_mutex_unlock
但由于 sem_t 的实现对用户隐藏所以所谓的、--操作只能通过函数来实现而不能直接、--符号。
信号量的初值决定了占用信号量的线程的个数。5.2 sem_init 函数 初始化一个信号量 int sem_init(sem_t *sem, int pshared, unsigned int value);参 1sem 信号量
参 2pshared 取 0 用于线程间取非 0一般为 1用于进程间
参 3value 指定信号量初值5.3 sem_destroy 函数 销毁一个信号量 int sem_destroy(sem_t *sem);5.4 sem_wait 函数 给信号量加锁 – int sem_wait(sem_t *sem);5.5 sem_post 函数 给信号量解锁 int sem_post(sem_t *sem);5.6 sem_trywait 函数 尝试对信号量加锁 – (与 sem_wait 的区别类比 lock 和 trylock) int sem_trywait(sem_t *sem);5.7 sem_timedwait 函数 限时尝试对信号量加锁 – int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);参 2abs_timeout 采用的是绝对时间。
定时 1 秒
time_t cur time(NULL); 获取当前时间。
struct timespec t; 定义 timespec 结构体变量 t
t.tv_sec cur1; 定时 1 秒
t.tv_nsec t.tv_sec 100;
sem_timedwait(sem, t); 传参6.信号量举例
#includestdio.h
#include semaphore.h
#includestdlib.h
#includepthread.h
#includeunistd.hsem_t sem;
void *func(void* arg){int i(int)arg;while(1){sem_wait(sem);printf(我是%d,我正在吃饭,当前座位剩余:%d\n,i,sem);sleep(2);sem_post(sem);sleep(4);}return NULL;
}int main()
{pthread_t tid[10];sem_init(sem,0,5);int i;for(i0;i10;i){pthread_create(tid[i],NULL,func,(void*)i);}for(i0;i10;i){pthread_join(tid[i],NULL);}sem_destroy(sem);return 0;
}zhaoxrzhaoxr-ThinkPad-E450:~/pthread$ ./sem
我是0,我正在吃饭,当前座位剩余:4
我是1,我正在吃饭,当前座位剩余:3
我是2,我正在吃饭,当前座位剩余:2
我是3,我正在吃饭,当前座位剩余:1
我是4,我正在吃饭,当前座位剩余:0
我是5,我正在吃饭,当前座位剩余:1
我是8,我正在吃饭,当前座位剩余:2
我是6,我正在吃饭,当前座位剩余:0
我是7,我正在吃饭,当前座位剩余:1
我是9,我正在吃饭,当前座位剩余:0
我是0,我正在吃饭,当前座位剩余:4
我是4,我正在吃饭,当前座位剩余:2
我是1,我正在吃饭,当前座位剩余:3
我是3,我正在吃饭,当前座位剩余:1
我是2,我正在吃饭,当前座位剩余:0
^C7.生产者消费者信号量模型 使用信号量完成线程间同步模拟生产者消费者问题。 【sem_product_consumer.c】
规定 如果□中有数据生产者不能生产只能阻塞。如果□中没有数据消费者不能消费只能等待数据。定义两个信号量S 满 0 S 空 1 S 满代表满格的信号量S 空表示空格的信号量程序起始格子一定为空所以有
T 生产者主函数 { sem_wait(S 空); //空格减1生产....sem_post(S 满); //大饼加1
}
T 消费者主函数 {sem_wait(S 满);//大饼减1消费....sem_post(S 空);//空格加1}假设 线程到达的顺序是:T 生、T 生、T 消。
那么 T 生 1 到达将 S 空-1生产将 S 满1
T 生 2 到达S 空已经为 0 阻塞
T 消 到达将 S 满-1消费将 S 空1三个线程到达的顺序是T 生 1、T 生 2、T 消。而执行的顺序是 T 生 1、T 消、T 生 2
这里S 空 表示空格子的总数代表可占用信号量的线程总数--1。其实这样的话信号量就等同于互斥锁。
但如果 S 空2、3、4……就不一样了该信号量同时可以由多个线程占用不再是互斥的形式。
因此我们说信号量是互斥锁的加强版。8.哲学家吃饭问题
让所有哲学家听到吃饭口令之后都拿自己右手边的筷子但是选定某一个哲学家A必须拿自己左手边的筷子
这样子A哲学家右边的哲学家B肯定可以第一个吃到饭吃完饭放下筷子下面一个人就可以吃饭了
以此类推。。。9.共享内存
#includestdio.h
#includepthread.h
#includestdlib.h
#includeunistd.hint a10;
void* func(void* arg)
{sleep(1);a100;printf(我要修改a\n);sleep(2);return NULL;
}
int main()
{pthread_t tid;pthread_create(tid,NULL,func,NULL);while(1){printf(%d\n,a);sleep(1);}pthread_join(tid,NULL);return 0;
}zhaoxrzhaoxr-ThinkPad-E450:~/pthread$ ./pthread_com
10
我要修改a
100
100
100
100
100
100
100
^C