第三十九章 POSIX信号量与互斥锁
POSIX信号量相关函数
sem_open
功能:
initialize and open a named semaphore
原型:
sem_t *sem_open(const char *name, int oflag);
参数:
name : 信号量的名字
oflag :
返回值:
成功 : 返回新信号量的地址
失败 : SEM_FAILED errno
Link with -pthread
sem_close
功能:
close a named semaphore
原型:
int sem_close(sem_t *sem);
参数:
sem : 信号量
返回值:
成功 : 0
失败 : -1 errno
Link with -pthread
sem_unlink
功能:
remove a named semaphore
原型:
int sem_unlink(const char *name);
参数:
name : 信号量
返回值:
成功 : 0
失败 : -1 errno
Link with -pthread
sem_init
功能:
initialize an unnamed semaphore
原型:
int sem_init(sem_t *sem, int pshared, unsigned int value);
参数:
sem : 信号量
pshared : 指示此信号量是在进程的线程之间还是在进程之间共享。
0 信号量在进程的线程之间共享,并且应位于所有线程可见的某个地址处(例如,全局变量或在堆上动态分配的变量)。
非零 则信号量在进程之间共享,并且应位于共享内存的区域中,任何可以访问共享的进程内存区域可以使用sem_post(3),sem_wait(3)等对信号量进行操作。
value : 初始值
返回值:
成功 : 0
失败 : -1 errno
注意:
虽然初始化的是一个匿名的信号量,它也可以用于不同进程间的多个线程间通信,取决于pshared非0和sem存放在共享内存中
Link with -pthread
sem_destroy
功能:
destroy an unnamed semaphore
原型:
int sem_destroy(sem_t *sem);
参数:
sem : 信号量
返回值:
成功 : 0
失败 : -1 errno
Link with -pthread
sem_wait
功能:
lock a semaphore
原型:
int sem_wait(sem_t *sem);
参数:
sem : 信号量
返回值:
成功 : 0
失败 : -1 errno
Link with -pthread
sem_post
功能:
unlock a semaphore
原型:
int sem_post(sem_t *sem);
参数:
sem : 信号量
返回值:
成功 : 0
失败 : -1 errno
Link with -pthread
POSIX互斥锁相关函数
pthread_mutex_init
功能:
initialize a mutex
原型:
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
参数:
mutex : 互斥锁
attr : 指定了新建互斥锁的属性。如果参数attr为空,则使用默认的互斥锁属性,默认属性为快速互斥锁
返回值:
成功 : 0
失败 : 返回对应的错误码
pthread_mutex_lock
功能:
lock a mutex
原型:
int pthread_mutex_lock(pthread_mutex_t *mutex);
参数:
mutex : 互斥锁
返回值:
成功 : 0
失败 : 返回对应的错误码
pthread_mutex_unlock
功能:
lock a mutex
原型:
int pthread_mutex_unlock(pthread_mutex_t *mutex);
参数:
mutex : 互斥锁
返回值:
成功 : 0
失败 : 返回对应的错误码
pthread_mutex_destroy
功能:
destroy a mutex
原型:
int pthread_mutex_destroy(pthread_mutex_t *mutex);
参数:
mutex : 互斥锁
返回值:
成功 : 0
失败 : 返回对应的错误码
生产者消费者问题
pctest.c
#include <unistd.h>
#include <sys/types.h>
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <semaphore.h>
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while (0)
#define CONSUMERS_COUNT 1 //消费者线程的个数
#define PRODUCERS_COUNT 5 //生产者线程的个数
#define BUFFSIZE 10 //缓冲区的大小
int g_buffer[BUFFSIZE]; //存放产品的ID的缓冲区
unsigned short in = 0; //产品的生产位置
unsigned short out = 0; //产品的消费位置
unsigned short produce_id = 0; //当前正在生产的产品的id
unsigned short consume_id = 0; //当前正在消费的产品的id
sem_t g_sem_full; //buffer满的信号量
sem_t g_sem_empty; //buffer空的信号量
pthread_mutex_t g_mutex;
pthread_t g_thread[CONSUMERS_COUNT + PRODUCERS_COUNT]; //总线程数
void *consume(void *arg)
{
int i;
int num = (int)arg;
while (1)
{
printf("%d waiting buffer not empty\n", num);
sem_wait(&g_sem_empty);
pthread_mutex_lock(&g_mutex);
for (i = 0; i < BUFFSIZE; ++i)
{
printf("%02d ", i);
if (g_buffer[i] == -1)
printf("%s", "null");
else
printf("%d ", g_buffer[i]);
if (i == out)
{
printf("\t <-- consume");
}
printf("\n");
}
consume_id = g_buffer[out];
printf("%d begin consume producet %d \n", num, consume_id);
g_buffer[out] = -1;
out = (out + 1) % BUFFSIZE;
printf("%d end consume producet %d \n", num, consume_id++);
pthread_mutex_unlock(&g_mutex);
sem_post(&g_sem_full);
sleep(1);
}
return NULL;
}
void *produce(void *arg)
{
int i;
int num = (int)arg;
while (1)
{
printf("%d waiting buffer not full\n", num);
sem_wait(&g_sem_full);
pthread_mutex_lock(&g_mutex);
for (i = 0; i < BUFFSIZE; ++i)
{
printf("%02d ", i);
if (g_buffer[i] == -1)
printf("%s", "null");
else
printf("%d ", g_buffer[i]);
if (i == in)
{
printf("\t <-- produce");
}
printf("\n");
}
printf("%d begin produce producet %d \n", num, produce_id);
g_buffer[in] = produce_id;
in = (in + 1) % BUFFSIZE;
printf("%d end produce producet %d \n", num, produce_id++);
pthread_mutex_unlock(&g_mutex);
sem_post(&g_sem_empty);
sleep(5);
}
return NULL;
}
int main()
{
int i;
for (i = 0; i < BUFFSIZE; ++i)
{
g_buffer[i] = -1;
}
sem_init(&g_sem_full, 0, BUFFSIZE);
sem_init(&g_sem_empty, 0, 0);
pthread_mutex_init(&g_mutex, NULL);
for (i = 0; i < CONSUMERS_COUNT; ++i)
{
pthread_create(&g_thread[i], NULL, consume, (void *)i);
}
for (i = 0; i < PRODUCERS_COUNT; ++i)
{
pthread_create(&g_thread[CONSUMERS_COUNT + i], NULL, produce, (void *)i);
}
for (i = 0; i < CONSUMERS_COUNT + PRODUCERS_COUNT; ++i)
{
pthread_join(g_thread[i], NULL);
}
sem_destroy(&g_sem_full);
sem_destroy(&g_sem_empty);
pthread_mutex_destroy(&g_mutex);
return 0;
}
自旋锁
- 自旋锁类似于互斥锁,它的性能比互斥锁更高
- 自旋锁与互斥锁很重要的一个区别是,线程在申请自旋锁的时候,线程不会被挂起,它处于忙等待的状态
pthread_spin_init
功能:
initialize a spin lock object
原型:
int pthread_spin_init(pthread_spinlock_t *lock, int pshared);
参数:
lock : 自旋锁
pshared :
PTHREAD_PROCESS_SHARED 允许任何有权访问分配了旋转锁的内存的线程对旋转锁进行操作,即使已分配该旋转锁在多个进程共享的内存中
PTHREAD_PROCESS_PRIVATE 只能由在与初始化旋转锁的线程相同的进程中创建的线程操作
返回值:
成功 : 0
失败 : 返回错误编号
pthread_spin_destroy
功能:
destroy a spin lock object
原型:
int pthread_spin_destroy(pthread_spinlock_t *lock);
参数:
lock : 自旋锁
返回值:
成功 : 0
失败 : 返回错误编号
pthread_spin_lock
功能:
lock a spin lock object
原型:
int pthread_spin_destroy(pthread_spinlock_t *lock);
参数:
lock : 自旋锁
返回值:
成功 : 0
失败 : errno
pthread_spin_unlock
功能:
unlock a spin lock object
原型:
int pthread_spin_unlock(pthread_spinlock_t *lock);
参数:
lock : 自旋锁
返回值:
成功 : 0
失败 : errno
spinlockvsmutex1.cc
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <errno.h>
#include <sys/time.h>
#include <list>
#include <pthread.h>
#define LOOPS 50000000
using namespace std;
list<int> the_list;
#ifdef USE_SPINLOCK
pthread_spinlock_t spinlock;
#else
pthread_mutex_t mutex;
#endif
//Get the thread id
pid_t gettid() { return syscall( __NR_gettid ); }
void *consumer(void *ptr)
{
int i;
printf("Consumer TID %lun", (unsigned long)gettid());
while (1)
{
#ifdef USE_SPINLOCK
pthread_spin_lock(&spinlock);
#else
pthread_mutex_lock(&mutex);
#endif
if (the_list.empty())
{
#ifdef USE_SPINLOCK
pthread_spin_unlock(&spinlock);
#else
pthread_mutex_unlock(&mutex);
#endif
break;
}
i = the_list.front();
the_list.pop_front();
#ifdef USE_SPINLOCK
pthread_spin_unlock(&spinlock);
#else
pthread_mutex_unlock(&mutex);
#endif
}
return NULL;
}
int main()
{
int i;
pthread_t thr1, thr2;
struct timeval tv1, tv2;
#ifdef USE_SPINLOCK
pthread_spin_init(&spinlock, 0);
#else
pthread_mutex_init(&mutex, NULL);
#endif
// Creating the list content...
for (i = 0; i < LOOPS; i++)
the_list.push_back(i);
// Measuring time before starting the threads...
gettimeofday(&tv1, NULL);
pthread_create(&thr1, NULL, consumer, NULL);
pthread_create(&thr2, NULL, consumer, NULL);
pthread_join(thr1, NULL);
pthread_join(thr2, NULL);
// Measuring time after threads finished...
gettimeofday(&tv2, NULL);
if (tv1.tv_usec > tv2.tv_usec)
{
tv2.tv_sec--;
tv2.tv_usec += 1000000;
}
printf("Result - %ld.%ldn", tv2.tv_sec - tv1.tv_sec,
tv2.tv_usec - tv1.tv_usec);
#ifdef USE_SPINLOCK
pthread_spin_destroy(&spinlock);
#else
pthread_mutex_destroy(&mutex);
#endif
return 0;
}
svm2.cc
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/syscall.h>
#define THREAD_NUM 2
pthread_t g_thread[THREAD_NUM];
#ifdef USE_SPINLOCK
pthread_spinlock_t g_spin;
#else
pthread_mutex_t g_mutex;
#endif
__uint64_t g_count;
pid_t gettid()
{
return syscall(SYS_gettid);
}
void *run_amuck(void *arg)
{
int i, j;
printf("Thread %lu started.n", (unsigned long)gettid());
for (i = 0; i < 10000; i++) {
#ifdef USE_SPINLOCK
pthread_spin_lock(&g_spin);
#else
pthread_mutex_lock(&g_mutex);
#endif
for (j = 0; j < 100000; j++) {
if (g_count++ == 123456789)
printf("Thread %lu wins!n", (unsigned long)gettid());
}
#ifdef USE_SPINLOCK
pthread_spin_unlock(&g_spin);
#else
pthread_mutex_unlock(&g_mutex);
#endif
}
printf("Thread %lu finished!n", (unsigned long)gettid());
return (NULL);
}
int main(int argc, char *argv[])
{
int i, threads = THREAD_NUM;
printf("Creating %d threads...n", threads);
#ifdef USE_SPINLOCK
pthread_spin_init(&g_spin, 0);
#else
pthread_mutex_init(&g_mutex, NULL);
#endif
for (i = 0; i < threads; i++)
pthread_create(&g_thread[i], NULL, run_amuck, (void *) i);
for (i = 0; i < threads; i++)
pthread_join(g_thread[i], NULL);
printf("Done.n");
return (0);
}
总结
Mutex适合对锁操作非常频繁的场景,并且具有更好的适应性。尽管相比spin lock它会花费更多的开销(主要是上下文切换),但是它能适合实际开发中复杂的应用场景,在保证一定性能的前提下提供更大的灵活度。
spin lock的lock/unlock性能更好(花费更少的cpu指令),但是它只适应用于临界区运行时间很短的场景。而在实际软件开发中,除非程序员对自己的程序的锁操作行为非常的了解,否则使用spin lock不是一个好主意(通常一个多线程程序中对锁的操作有数以万次,如果失败的锁操作(contended lock requests)过多的话就会浪费很多的时间进行空等待)。
更保险的方法或许是先(保守的)使用 Mutex,然后如果对性能还有进一步的需求,可以尝试使用spin lock进行调优。毕竟我们的程序不像Linux kernel那样对性能需求那么高(Linux Kernel最常用的锁操作是spin lock和rw lock)。
读写锁
- 只要没有线程持有给定的读写锁用于写,那么任意数目的线程可以持有读写锁用于读
- 仅当没有线程持有某个给定的读写锁用于读或用于写时,才能分配读写锁用于写
- 读写锁用于读 称为共享锁, 读写锁用于写 称为排它锁
pthread_rwlock_init
功能:
initialize a read-write lock object
原型:
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
const pthread_rwlockattr_t *restrict attr);
参数:
rwlock : 读写锁指针
attr : 读写锁属性指针
返回值:
成功 : 0
失败 : 返回错误码
pthread_rwlock_destroy
功能:
destroy a read-write lock object
原型:
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
参数:
rwlock : 读写锁指针
返回值:
成功 : 0
失败 : 返回错误码
pthread_rwlock_rdlock
功能:
lock a read-write lock object for reading
原型:
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
参数:
rwlock : 读写锁指针
返回值:
成功 : 0
失败 : 返回错误码
pthread_rwlock_wrlock
功能:
lock a read-write lock object for writing
原型:
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
参数:
rwlock : 读写锁指针
返回值:
成功 : 0
失败 : 返回错误码
pthread_rwlock_unlock
功能:
unlock a read-write lock object
原型:
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
参数:
rwlock : 读写锁指针
返回值:
成功 : 0
失败 : 返回错误码
rw_test.cc
#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <pthread.h>
using namespace std;
struct{
pthread_rwlock_t rwlock;
int product;
}sharedData = {PTHREAD_RWLOCK_INITIALIZER, 0};
void * produce(void *ptr)
{
for (int i = 0; i < 5; ++i)
{
pthread_rwlock_wrlock(&sharedData.rwlock);
sharedData.product = i;
pthread_rwlock_unlock(&sharedData.rwlock);
sleep(1);
}
}
void * consume1(void *ptr)
{
for (int i = 0; i < 5;)
{
pthread_rwlock_rdlock(&sharedData.rwlock);
cout<<"consume1:"<<sharedData.product<<endl;
pthread_rwlock_unlock(&sharedData.rwlock);
++i;
sleep(1);
}
}
void * consume2(void *ptr)
{
for (int i = 0; i < 5;)
{
pthread_rwlock_rdlock(&sharedData.rwlock);
cout<<"consume2:"<<sharedData.product<<endl;
pthread_rwlock_unlock(&sharedData.rwlock);
++i;
sleep(1);
}
}
int main()
{
pthread_t tid1, tid2, tid3;
pthread_create(&tid1, NULL, produce, NULL);
pthread_create(&tid2, NULL, consume1, NULL);
pthread_create(&tid3, NULL, consume2, NULL);
void *retVal;
pthread_join(tid1, &retVal);
pthread_join(tid2, &retVal);
pthread_join(tid3, &retVal);
return 0;
}
总结
第三十九章 POSIX信号量与互斥锁的更多相关文章
- Gradle 1.12用户指南翻译——第三十九章. IDEA 插件
本文由CSDN博客万一博主翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...
- “全栈2019”Java第三十九章:构造函数、构造方法、构造器
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- “全栈2019”Java多线程第二十九章:可重入锁与不可重入锁详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- POSIX信号量与互斥锁实现生产者消费者模型
posix信号量 Link with -lpthread. sem_t *sem_open(const char *name, int oflag);//打开POSIX信号量 sem_t *sem_o ...
- linux网络编程之posix信号量与互斥锁
继上次学习了posix线程之后,这次来讨论一下posix信号量与互斥锁相关的知识: 跟posix消息队列,共享内存的打开,关闭,删除操作一样,不过,上面的函数是对有名信号量进行操作,通过man帮助可以 ...
- posix信号量与互斥锁
1.简介 POSIX信号量是一个sem_t 类型的变量,但POSIX 有两种信号量的实现机制:无名信号量和命名信号量.无名信号量可以用在共享内存的情况下, 比如实现进程中各个线程之间的互斥和同步.命名 ...
- linux网络编程-posix信号量与互斥锁(39)
-posix信号量信号量 是打开一个有名的信号量 sem_init是打开一个无名的信号量,无名信号量的销毁用sem_destroy sem_wait和sem_post是对信号量进行pv操作,既可以使用 ...
- 【WPF学习】第三十九章 理解形状
在WPF用户界面中,绘制2D图形内容的最简单方法是使用形状(shape)——专门用于表示简单的直线.椭圆.矩形以及多变形的一些类.从技术角度看,形状就是所谓的绘图图元(primitive).可组合这些 ...
- 第三十九章 微服务CICD(1)- gitlab搭建与使用(docker版)
一.下载docker镜像 前提:docker引擎已经安装好. docker pull gitlab/gitlab-ce gitlab是8.13.1版本. 二.启动应用 docker run -d -h ...
随机推荐
- Django之使用celery和NGINX生成静态页面实现性能优化
性能优化原理: 当我们要给client浏览器返回一个页面时,我们需要去数据库查询数据并将数据和基本页面模板渲染形成页面返回给客户端,但如果每一个用户访问时都去查询一次首页的的数据时,当日访问量很大时那 ...
- 同时支持EF+Dapper的混合仓储,助你快速搭建数据访问层
背景 17年开始,公司开始向DotNet Core转型,面对ORM工具的选型,当时围绕Dapper和EF发生了激烈的讨论.项目团队更加关注快速交付,他们主张使用EF这种能快速开发的ORM工具:而在线业 ...
- css 实现图片灰度
先看效果鼠标移入图片中摁下向左移动 图片由灰度变为原图 向右移动原图变灰度 代码如下:尚未做优化 <style> *{ margin:0; padding:0; } #img{ w ...
- Nginx internal 指令限制访问图片资源文件
Nginx 的 internal 指令可以用来限制 Web 公共目录下的图片等资源文件被任意用户直接访问.一个明显的使用场景是,对于用户上传的认证图片,属于个人隐私资源,不应该让所有用户都能访问得到, ...
- 深圳市网络安全中心发出通告,TeamViewer已被APT41黑客攻破
上期我们讲了东南亚赌局为什么都是福建老板了,这次来介绍下黑客组织APT41 ,这个组织在 HT界 比较出名,很早之前是匿名在地下交易所的,而在近年频繁出现在大众视野中,这不,刚刚又把我们常用的远程工具 ...
- springboot项目目录结构
idea新建springboot项目 按默认下一步至完成,默认目录结构如下 pom.xml文件内容如下 <?xml version="1.0" encoding=" ...
- day 19作业
目录 今日作业: 今日作业: 1.什么是对象?什么是类? 答:对象是特征与技能的集合体,类是一系列对象相同的特征与技能的结合体 2.绑定方法的有什么特点 答:由对象来调用称之为对象的绑定方法,不同的对 ...
- python编程基础之三十八
正则表达式:正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符.及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑. 需要引入内置模块r ...
- django渲染高阶
08.16自我总结 django渲染高阶 一.利用母版渲染 1.创建母版文件 如:stamper.html <!DOCTYPE html> <html lang="en&q ...
- string字符串转数组
/** * THis_is_a_cat * This Is A Cat * * Cat A Is This * @author Administrator * */ public class Test ...