线程同步 - POSIX互斥锁
线程同步 - POSIX互斥锁
概括
本文讲解POSIX中互斥量的基本用法,从而能达到简单的线程同步。互斥量是一种特殊的变量,它有两种状态:锁定以及解锁。如果互斥量是锁定的,就有一个特定的线程持有或者拥有这个互斥量;如果没有线程持有这个互斥量,我们就说这个互斥量是解锁的、可用的。同时,互斥量还有一个等待持有该互斥量的线程队列。互斥队列中的线程获得互斥量的顺序由线程调度所决定,但POSIX没有要求实现任何特定的策略。
程序描述
现在我们尝试写一个程序来体会互斥量的基本应用,用程序来模拟验证:$$\int_0^1sinx dx = 1.0 - cos1 $$ 利用多个子线程来产生[0,1]之间的随机数,每产生一次count则增加一,并且将产生的数加入sum,然后用sum/count来模拟等式左边,最后计算等式右边作为标准值,然后计算误差。
程序用法:从命令行参数中接受要创建的线程数目以及运行时间,即$ ./程序名 线程数 等待时间。
全局变量设计:
static int doneflag = 0;
static int count = 0;
static double sum = 0;
static pthread_mutex_t flaglock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t sumlock = PTHREAD_MUTEX_INITIALIZER;
用sumlock来守护count和sum,用flaglock来守护doneflag。
main函数里创建线程数组,然后用pthread_create来创建线程,用pthread_join来等待线程。关键语句:
num_threads = atoi(argv[1]);
tids = (pthread_t *)calloc(num_threads, sizeof(pthread_t);
for (i = 0; i < num_threads; ++i) /* 创建num_threads个compute_thread线程 */
pthread_create(tids + i, NULL, compute_thread, NULL);
线程函数
注意创建的线程处理函数的形式:void *(*start_routine) (void *), 将函数指针(函数名)传递给pthread_create即可。子进程循环计算,直到doneflag == 1。
其它函数
randsafe():用一个互斥量来保护rand(),要确保不会有两个线程同时调用rand,因为它在多线程中是不安全的。其次,rand不是一个特别好的伪随机数生成器,所以应该在实际程序中避免使用它,这里只是用作demo示范。
set_done和get_done分别用于设置flag和获得flag。
最后show_results来整理数据并进行输出。
运行情况
$ gcc -o demo mutex_demo.c -lm -lpthread
$ ./demo 5 1
The sum is 232.913662 and the count is 500
The average is 0.465827 and error is 0.006130 or 1.333405%
$ ./demo 10 1
The sum is 467.382538 and the count is 1000
The average is 0.467383 and error is 0.007685 or 1.671717%
$ ./demo 10 2
The sum is 919.177364 and the count is 1990
The average is 0.461898 and error is 0.002200 or 0.478680%
源代码
/**
* @Description: 利用子线程计算0-1范围内正弦函数的平均值,并与实际值进行误差比较。
*/
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <time.h>
#include <unistd.h>
#define TEM_MILLION 10000000L
static int doneflag = 0;
static int count = 0;
static double sum = 0;
static pthread_mutex_t flaglock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t sumlock = PTHREAD_MUTEX_INITIALIZER;
/* 线程函数, 计算随机和 */
void *compute_thread(void *arg1);
int set_done(void);
int get_done(int *flag);
int randsafe(double *valp);
int add(double x);
int show_results(void);
int main(int argc, char *argv[])
{
int i;
int num_threads;
int sleep_time;
pthread_t *tids;
if (argc != 3) {
fprintf(stderr, "Usage: %s num_threads sleep_time\n", argv[0]);
return 1;
}
num_threads = atoi(argv[1]);
sleep_time = atoi(argv[2]);
if ((tids = (pthread_t *)calloc(num_threads, sizeof(pthread_t))) == NULL) {
perror("Failed to allocate space for thread IDs");
return 1;
}
for (i = 0; i < num_threads; ++i) /* 创建num_threads个compute_thread线程 */
pthread_create(tids + i, NULL, compute_thread, NULL);
sleep(sleep_time);
set_done();
for (i = 0; i < num_threads; ++i) /* 等待线程完成 */
pthread_join(tids[i], NULL);
if (show_results())
return 1;
return 0;
}
/* 线程函数, 计算随机和 */
void *compute_thread(void *arg1) {
int localdone = 0;
struct timespec sleep_local;
double val;
sleep_local.tv_sec = 0;
sleep_local.tv_nsec = TEM_MILLION; /* 10ms */
while (!localdone) {
randsafe(&val);
add(sin(val));
get_done(&localdone);
nanosleep(&sleep_local, NULL); /* 让其他线程进入 */
}
}
int set_done(void) {
pthread_mutex_lock(&flaglock);
doneflag = 1;
return pthread_mutex_unlock(&flaglock);
}
int get_done(int *flag) {
pthread_mutex_lock(&flaglock);
*flag = doneflag;
return pthread_mutex_unlock(&flaglock);
}
int randsafe(double *valp) {
static pthread_mutex_t randlock = PTHREAD_MUTEX_INITIALIZER;
*valp = (double)rand() / (double)RAND_MAX;
return pthread_mutex_unlock(&randlock);
}
int add(double x) {
pthread_mutex_lock(&sumlock);
sum += x;
count++;
return pthread_mutex_unlock(&sumlock);
}
int show_results(void) {
int res_count;
double res_sum;
double calculated;
double average;
double err;
double perr;
pthread_mutex_lock(&sumlock);
res_sum = sum;
res_count = count;
pthread_mutex_unlock(&sumlock);
if (count == 0)
printf("No values were summed.\n");
else {
calculated = 1.0 - cos(1.0);
average = sum/count;
err = average - calculated;
perr = 100.0*err/calculated;
printf("The sum is %f and the count is %d\n", sum, count);
printf("The average is %f and error is %f or %f%%\n", average, err, perr);
}
return 0;
}
线程同步 - POSIX互斥锁的更多相关文章
- APUE学习笔记——11 线程同步、互斥锁、自旋锁、条件变量
线程同步 同属于一个进程的不同线程是共享内存的,因而在执行过程中需要考虑数据的一致性. 假设:进程有一变量i=0,线程A执行i++,线程B执行i++,那么最终i的取值是多少呢?似乎一定 ...
- UNIX环境高级编程——线程同步之互斥锁、读写锁和条件变量(小结)
一.使用互斥锁 1.初始化互斥量 pthread_mutex_t mutex =PTHREAD_MUTEX_INITIALIZER;//静态初始化互斥量 int pthread_mutex_init( ...
- ReactiveSwift源码解析(十一) Atomic的代码实现以及其中的Defer延迟、Posix互斥锁、递归锁
本篇博客我们来聊一下ReactiveSwift中的原子性操作,在此内容上我们简单的聊一下Posix互斥锁以及递归锁的概念以及使用场景.然后再聊一下Atomic的代码实现.Atomic主要负责多线程下的 ...
- Python进阶----线程基础,开启线程的方式(类和函数),线程VS进程,线程的方法,守护线程,详解互斥锁,递归锁,信号量
Python进阶----线程基础,开启线程的方式(类和函数),线程VS进程,线程的方法,守护线程,详解互斥锁,递归锁,信号量 一丶线程的理论知识 什么是线程: 1.线程是一堆指令,是操作系统调度 ...
- win32进阶必备:多线程同步之互斥锁
应用多线程互斥锁之前首先简单过一下C程序可能用到的3个创建线程函数: CreateThread,windows系统提供的唯一创建线程API,_beginthread和_beginthreadex都在内 ...
- UNIX环境高级编程——线程同步之互斥量
互斥量(也称为互斥锁)出自POSIX线程标准,可以用来同步同一进程中的各个线程.当然如果一个互斥量存放在多个进程共享的某个内存区中,那么还可以通过互斥量来进行进程间的同步. 互斥量,从字面上就可以知道 ...
- linux线程同步(1)-互斥量
一.概述 互斥量是线程同步的一种机制,用来保护多线程的共享资源.同一时刻,只允许一个线程对临界区进行 ...
- exec函数族,守护进程,线程同步和互斥
2015.3.2 进程和程序有三点不同:1,存在位置不同,程序:硬盘,磁盘.进程:内存2. 程序是静态的,进程是动态的 执行./a.out -->bash->bash程序调用fork()- ...
- node源码详解(七) —— 文件异步io、线程池【互斥锁、条件变量、管道、事件对象】
本作品采用知识共享署名 4.0 国际许可协议进行许可.转载保留声明头部与原文链接https://luzeshu.com/blog/nodesource7 本博客同步在https://cnodejs.o ...
随机推荐
- Android开发实战——记账本(4)
开发日志(4)——MainActivity 在MainActivity中编写了几个方法.首先,点击账本的一条记录可以选择删除他,然后重写了fab,使之在点击他后能够添加记录.还写了删除全部记录的方法. ...
- Attribute "resultType" must be declared for element type "update" or "insert"
仔细查看错误如图所示: 解决错误就是把resultType去掉,因为在insert和update语句中是没有返回值的.小坑小坑 转自:https://blog.csdn.net/u013144287/ ...
- WIN10 设置WEB
Web服务器搭建步骤(Win10) 1.在“开始”菜单处打开“控制面板”. 2点击“程序”. 3.点击“启动或关闭Windows功能”. 4.对“Internet Information Servic ...
- Tarjan-SCC-NOIP2015message
This article is made by Jason-Cow.Welcome to reprint.But please post the writer's address. http://ww ...
- opencv:全局阈值
图像的二值化分割,最重要的就是计算阈值 阈值的计算方法很多,基本分为两类,全局阈值与自适应阈值 OTSU.Triangle #include <opencv2/opencv.hpp> #i ...
- 自己常用的Linux命令和Hadoop命令
记录自己常用的Linux命令: ss的启动命令:ssserver -c /etc/shadowsocks.json jupyter notebook的启动命令:jupyter notebook --a ...
- Educational Codeforces Round 78 (Rated for Div. 2)E(构造,DFS)
DFS,把和当前结点相连的点全都括在当前结点左右区间里,它们的左端点依次++,然后对这些结点进行DFS,优先对左端点更大的进行DFS,这样它右端点会先括起来,和它同层的结点(后DFS的那些)的区间会把 ...
- iptables详解(3):增删改存
总结一下iptables规则管理的增删改存命令: 1.添加规则: 1)在指定表的指定链的末尾添加一条规则,-A选项表示在末尾添加,-j表示采取的动作,例如DROP.REJECT.ACCEPT 命令语法 ...
- Springmvc-crud-04错误(路径变量)
错误: 原因:接收不到restful风格请求的参数(id值),需要添加路径变量注解 @RequestMapping(value="/book/{id}",method=Reques ...
- 安卓基础(AndroidViewModel)
今天学习了AndroidViewModel,但是我根据视频上讲解,根据所讲用例,在添加依赖得时候一直报错,后来我请教大佬,他告诉我说是,因为网络得问题,国外得一些依赖有可能下不下来,所以可以下载阿里云 ...