Linux线程(五)

文章目录

  • Linux线程(五)
    • 一、posix信号量
    • 二:生产者消费者模型
    • 三、读者写锁问题

一、posix信号量

  • 1.相关概念:

  • posix信号量和system V信号量作用一样,都是用于同步操作,达到无冲突访问共享资源的目的。但Posix可以用于线程间同步

  • 2.sem_init函数

int sem_init(sem_t *sem, int pshared, unsigned int value)
  • 功能:初始化信号量
  • 参数:
sem_t *sem:待初始化的信号量
int pshared:如果为0表示只能在当前进程内线程间共享;如果是非0,表示进程间共享
value:给信号量设的初始值
  • 3.sem_destroy函数
int sem_destroy(sem_t *sem);
  • 作用:销毁一个信号量

  • 4.sem_post函数

int sem_post(sem_t *sem);
  • 功能:发布信号量,表示资源使用完毕,可以归还资源了。将信号量的值 + 1
  • 5.sem_wait函数
int sem_wait(sem_t *sem);
  • 功能:等待信号量,会将信号量的值 — 1;

二:生产者消费者模型

上次生产者消费者模型是基于queue实现的,其空间是可以动态分配的,现在基于固定大小的环形队列重写这个代码

#include <iostream>
#include <vector>
#include <stdlib.h>
#include <semaphore.h>
#include <pthread.h>
#define NUM 16class RingQueue{private:std::vector<int> q;int cap;sem_t data_sem;sem_t space_sem;int consume_step;int product_step;
public:RingQueue(int _cap = NUM):q(_cap),cap(_cap){sem_init(&data_sem, 0, 0);sem_init(&space_sem, 0, cap);consume_step = 0;product_step = 0;} void PutData(const int &data){sem_wait(&space_sem); // Pq[consume_step] = data;consume_step++;consume_step %= cap;sem_post(&data_sem); //V}void GetData(int &data){sem_wait(&data_sem);data = q[product_step];product_step++;product_step %= cap;sem_post(&space_sem);} ~RingQueue(){sem_destroy(&data_sem);sem_destroy(&space_sem);}
};void *consumer(void *arg)
{RingQueue *rqp = (RingQueue*)arg;int data;for( ; ; ){rqp->GetData(data);std::cout << "Consume data done : " << data << std::endl;sleep(1);}
} //more faster
void *producter(void *arg)
{RingQueue *rqp = (RingQueue*)arg;srand((unsigned long)time(NULL));for( ; ; ){int data = rand() % 1024;rqp->PutData(data);std::cout << "Prodoct data done: " << data << std::endl;// sleep(1);}
} int main()
{RingQueue rq;pthread_t c,p;pthread_create(&c, NULL, consumer, (void*)&rq);pthread_create(&p, NULL, producter, (void*)&rq);pthread_join(c, NULL);pthread_join(p, NULL);return 0;
}

三、读者写锁问题

  • 在编写多线程的时候,有一种情况是十分常见的。那就是,有些公共数据修改的机会比较少。相比较改写,它们读的机会反而高的多。
  • 通常而言,在读的过程中,往往伴随着查找的操作,中间耗时很长。给这种代码段加锁,会极大地降低我们程序的效率。
  • 那么有没有一种方法,可以专门处理这种多读少写的情况呢? 有,那就是读写锁
  • 1.pthread_rwlock_init函数
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr);
  • 功能:初始化读写锁
  • 参数1:初始化的读写锁
  • 参数2:读写锁初始化时的属性。如果用默认属性,此处填NULL
  • 2.pthread_rwlock_destory函数
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock)
  • 功能:销毁一个读写锁
  • 如果在pthread_rwlock_destory之前就释放了读写锁占用的内存空间,那么分配给这个锁的资源就会丢失
  • 备注(重点):此函数只是反初始化读写锁变量,并没有释放内存空间,如果读写锁变量是通过malloc等函数申请的,那么需要在free掉读写锁变量之前调用pthread_rwlock_destory函数
  • 3.pthread_rwlock_rdlock函数
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
  • 功能:在读模式下锁定读写锁
  • 4.pthread_rwlock_wrlock函数
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
  • 功能:在写模式下锁定读写锁
  • 5.pthread_rwlock_unlock函数
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
  • 不管以何种方式锁住读写锁,都可以用这个函数解锁
  • 各种实现可能会对共享模式下可获取的读写锁的次数进行限制,所以需要检查pthread_rwlock_rdlock的返回值
  • 即使pthread_rwlock_wrlock和pthread_rwlock_unlock有错误返回,而且从技术上来讲,在调用函数时应该总会检查错误返回,但是如果锁设计合理的话,就不需要检查它们。
  • 错误返回值的定义只是针对不正确使用读写锁的情况(如未经初始化的锁),或者试图获取已拥有的锁从而可能产生死锁的情况
  • 但是还需要注意,有些特定的实现可能会定义另外的错误返回
  • 6.设置读写锁优先
int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t *attr, int pref);
  • pref 共有 3 种选择
 - PTHREAD_RWLOCK_PREFER_READER_NP (默认设置) 读者优先,可能会导致写者饥饿情况- PTHREAD_RWLOCK_PREFER_WRITER_NP 写者优先,目前有 BUG,导致表现行为和PTHREAD_RWLOCK_PREFER_READER_NP 一致- PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP 写者优先,但写者不能递归加锁

测试代码:

#include <vector>
#include <sstream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <pthread.h>volatile int ticket = 1000;
pthread_rwlock_t rwlock;
void * reader(void * arg)
{char *id = (char *)arg;while (1) {pthread_rwlock_rdlock(&rwlock);if (ticket <= 0) {pthread_rwlock_unlock(&rwlock);break;}printf("%s: %d\n", id, ticket);pthread_rwlock_unlock(&rwlock);usleep(1);} return nullptr;
}void * writer(void * arg)
{char *id = (char *)arg;while (1) {pthread_rwlock_wrlock(&rwlock);if (ticket <= 0) {pthread_rwlock_unlock(&rwlock);break;} printf("%s: %d\n", id, --ticket);pthread_rwlock_unlock(&rwlock);usleep(1);}return nullptr;
}struct ThreadAttr
{pthread_t tid;std::string id;
};std::string create_reader_id(std::size_t i)
{// 利用 ostringstream 进行 string 拼接std::ostringstream oss("thread reader ", std::ios_base::ate);
oss << i;return oss.str();
} std::string create_writer_id(std::size_t i)
{// 利用 ostringstream 进行 string 拼接std::ostringstream oss("thread writer ", std::ios_base::ate);oss << i;return oss.str();
}void init_readers(std::vector<ThreadAttr>& vec)
{for (std::size_t i = 0; i < vec.size(); ++i) {vec[i].id = create_reader_id(i);pthread_create(&vec[i].tid, nullptr, reader, (void *)vec[i].id.c_str());}
}void init_writers(std::vector<ThreadAttr>& vec)
{for (std::size_t i = 0; i < vec.size(); ++i) {vec[i].id = create_writer_id(i);pthread_create(&vec[i].tid, nullptr, writer, (void *)vec[i].id.c_str());}
} void join_threads(std::vector<ThreadAttr> const& vec)
{// 我们按创建的 逆序 来进行线程的回收for (std::vector<ThreadAttr>::const_reverse_iterator it = vec.rbegin(); it !=vec.rend(); ++it) {pthread_t const& tid = it->tid;pthread_join(tid, nullptr);}
}void init_rwlock()
{ #if 0 // 写优先pthread_rwlockattr_t attr;pthread_rwlockattr_init(&attr);pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);pthread_rwlock_init(&rwlock, &attr);pthread_rwlockattr_destroy(&attr);#else // 读优先,会造成写饥饿pthread_rwlock_init(&rwlock, nullptr);#endif
} int main()
{// 测试效果不明显的情况下,可以加大 reader_nr// 但也不能太大,超过一定阈值后系统就调度不了主线程了const std::size_t reader_nr = 1000;const std::size_t writer_nr = 2;std::vector<ThreadAttr> readers(reader_nr);std::vector<ThreadAttr> writers(writer_nr);init_rwlock();init_readers(readers);init_writers(writers);join_threads(writers);join_threads(readers);pthread_rwlock_destroy(&rwlock);
}

Linux线程(五)相关推荐

  1. Linux线程-互斥锁pthread_mutex_t

    Linux线程-互斥锁pthread_mutex_t 在线程实际运行过程中,我们经常需要多个线程保持同步.这时可以用互斥锁来完成任务:互斥锁的使用过程中,主要有pthread_mutex_init, ...

  2. c++ linux 线程等待与唤醒_Linux线程同步(互斥量、信号量、条件变量、生产消费者模型)...

    为什么要线程同步? 线程间有很多共享资源,都对一个共享数据读写操作,线程操作共享资源的先后顺序不确定,可能会造成数据的冲突 看一个例子 两个线程屏行对全局变量count++ (采用一个val值作为中间 ...

  3. Linux线程(三)

    Linux线程(三) 文章目录 Linux线程(三) 一.互斥量 二..互斥量的接口 三.互斥量实现用原理探究 四.可重入VS线程安全 五.死锁 一.互斥量 根据前面的分析,得到的结果不是我们想要的原 ...

  4. linux 线程就绪态_动手使用Linux就绪的Dell XPS 13开发人员版

    linux 线程就绪态 大约15个月前,我在ASUS Zenbook UX305上回顾了Fedora 21 . 就像我对这台机器的满意一样,新的一年来了,我有机会买了一台新的个人笔记本电脑. 我发现自 ...

  5. linux线程池的使用

      Linux下通用线程池的创建与使用[ZT] 收藏 本文给出了一个通用的线程池框架,该框 架将与线程执行相关的任务进行了高层次的抽象,使之与具体的执行任务无关.另外该线程池具有动态伸缩性,它能根据执 ...

  6. Linux 线程间通信方式、进程通信方式

    Linux线程间通信几种主要的手段 1. 管道:         管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有 ...

  7. Linux/Unix五种IO模型

    文章目录 引入 操作系统的内核态和用户态 文件描述符fd IO操作过程: 阻塞和非阻塞 同步和异步 同步IO和异步IO 五种IO模型 1.(同步)阻塞IO模型 2.(同步)非阻塞IO模型 3.IO多路 ...

  8. Linux线程属性总结 http://blog.csdn.net/zsf8701/article/details/7842392

    Linux线程属性总结 分类: Linux 2012-08-08 11:05  3657人阅读  评论(2)  收藏  举报 linux thread solaris concurrency syst ...

  9. [转载]Linux 线程实现机制分析

    自从多线程编程的概念出现在 Linux 中以来,Linux 多线应用的发展总是与两个问题脱不开干系:兼容性.效率.本文从线程模型入手,通过分析目前 Linux 平台上最流行的 LinuxThreads ...

最新文章

  1. 用JS验证asp.net服务端控件
  2. 用流读取文件中的内容
  3. 目前微服务/REST的最佳技术栈
  4. 【CC精品教程】任务三:CC刺像控点,提交空三,新建重建项目(三维格网、三维点云、DOM和DSM)
  5. Hibernate基本概念 (5)
  6. 解决Qt graphis-view框架中,上层图元接收hover事件导致底层图元接收不到的问题
  7. 对应版本_Office 开发版本号与版本对应关系
  8. 每日一则----算法----二分查找法
  9. 11个让你吃惊的 Linux 终端命令
  10. 2017年度深圳市知识产权优势企业拟定名单公示(钱多,工资高的公司)
  11. neo4j 如何删除所以的节点和关系
  12. html5 小车动画_HTML5 实现小车动画效果(Canvas/CSS3/JQuery)
  13. EasyUI基础入门之Parser(解析器)
  14. 贝壳基于 Flink 的实时计算演进之路
  15. 【JS】Unicode编码
  16. 必须安装三星系列android系统智能手机usb驱动程序,三星N9109W Android 5.0 (GALAXY Note 4 电信4G)usb驱动下载安装教程...
  17. 平行交通:虚实互动的智能交通管理与控制
  18. 如何用DETR(detection transformer)训练自己的数据集
  19. 除了技校 哪里还可以学计算机技术,我打算去读技校,技校毕业了去工作有钱了,还可以读技校吗?(我想多学一门技术)...
  20. 教你五步实现KeyShot焦散线效果

热门文章

  1. JAVA标识符命名规则及命名规范
  2. 常用LINQ关键字用法汇总
  3. dos进入/退出某文件夹
  4. python 网络爬虫requests模块
  5. 想自己造无人机吗?Intel推出基于 Linux x86的自助无人机开发板
  6. [Windows] Windows 8.1 取消在任务栏显示应用商店的应用
  7. POJ - 2914 Minimum Cut(全局最小割-Stoer_Wagner)
  8. CodeForces - 364A Matrix(思维+数学)
  9. HDU - 3341 Lost's revenge(AC自动机+状压dp)
  10. HDU - 5335 Walk Out(bfs+路径输出+贪心)