线程同步 - 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 ...
随机推荐
- TCP协议的3次握手与4次挥手过程【深度详解】
一.前沿 尽管TCP和UDP都使用相同的网络层(IP),TCP却向应用层提供与UDP完全不同的服务.TCP提供一种面向连接的.可靠的字节流服务.面向连接意味着两个使用TCP的应用(通常是一个客户和一个 ...
- MP4转mp3
python实现: 依赖: glob,pydub "Couldn't find ffmpeg or avconv - defaulting to ffmpeg, but may not wo ...
- Mongodb学习笔记(四)管理
一.数据导出.导入 数据导入:mongoexport ./mongoimport -d 数据库 -c 集合 文件名 数据导出:mongoimport 参数说明: -d:指明使用的库,如text -c: ...
- 【音乐欣赏】《Fake》 - The Tech Thieves
曲名:Fake 作者:The Tech Thieves [01;12.092]I got wasted,learning how to let it out [01;24.009]Please don ...
- tomcat安装成功以后进行测试步骤:
tomcat安装成功以后进行测试步骤: 编写测试页面: 进入Tomcat安装对应路径: E:\Tomcat\apache-tomcat-8.5.45\webapps\ROOT 创建:test.jsp ...
- EntityFramework 插入自增ID主从表数据
原因: 数据库中的两个表是主从表关系,但是没有建外键,而表的id用的是数据库的自增整数,导致在使用EF导入主从表数据时,需要先保存主表数据,取到 主表的自增id后才能插入从表数据,这样循环之下,数据插 ...
- 一篇文章带你了解JavaScript中的变量,作用域和内存问题
1 在JavaScript中的变量分别区分为两种: 一种为基本类型值,一种为应用类型值. 基本类型值指的是简单的数据段 引用类型值为可能由多个值组成的对象 引用类型的值是保存在内存中的对象,JavaS ...
- Bugku-CTF加密篇之zip伪加密(flag.zip)
zip伪加密
- 通过ping和tracert命令来判断网络经过多少个路由。trace和route合作
摘抄自: https://blog.csdn.net/foreverhuylee/article/details/49853075 当我们访问某个网络时,通过tracert命令,就能知道本机与目标主机 ...
- ubuntu mysql新增用户并开启远程连接
1.首先用root用户登录mysql mysql -u root -p 输入密码后登录成功 2.新建用户 use mysql; select host,user from user;(查看现有用户) ...