线程同步 - 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 ...
随机推荐
- C语言 fgets
C语言 fgets #include <stdio.h> char *fgets(char *s, int size, FILE *stream); 功能:从stream指定的文件内读入字 ...
- 2019年牛客多校第一场B题Integration 数学
2019年牛客多校第一场B题 Integration 题意 给出一个公式,求值 思路 明显的化简公式题,公式是分母连乘形式,这个时候要想到拆分,那如何拆分母呢,自然是裂项,此时有很多项裂项,我们不妨从 ...
- 数据存储 csv
# # 保存csv格式的数据import csv csvFile = open('test.csv','w+',newline='') #文本方式可读写 try: writer = csv.write ...
- jquery实现点击显示,再点击隐藏
//点击a标签,轮流显示和隐藏<div id="timo" style="background-color:red;height:50px;width:50px;& ...
- vs rdlc 设置Tablix 在新页面重复表头
设置方法: 1.选中Tablix控件 2.点开三角形 3.选择高级模式 4.在行组 下 选择静态,然后看右边的属性 5.将属性设置为如下 就可以让Tablix控件实现在新页中带表头
- ALSA driver基本概念
https://blog.csdn.net/zyuanyun/article/details/59180272#t6 1.Card For each soundcard, a “card” recor ...
- DOMException: Failed to execute 'open' on 'XMLHttpRequest': Invalid URL 未能在“xmlhttpRequest”上执行“open”:无效的URL。
出现这个报错主要是baseurl:http://192.168.*.*/后面的(/)或是请求里面的url:/user/login中前面的(/)有一个漏掉了,导致合成的路径不完整,所以报错:无效的URL
- Python实现将图片以二进制格式保存到MySQL数据库中,以及取出:
创建数据库表格式: CREATE TABLE photo ( photo_no int(6) unsigned NOT NULL auto_increment, image MEDIUMBLOB, P ...
- 01hive基础操作
一. Hive基础概念 我自己本人一开始学习Hive的时候,最大的疑问就是hive和hbase到底有什么区别?(因为自己本身学校课程设置问题有了解到一丢丢hbase的知识) 所以先甩一篇博客提供给跟我 ...
- Git的安装和使用(转载)
转载博客连接:https://www.cnblogs.com/qijunjun/p/7137207.html 实际项目开发中,我们经常会用一些版本控制器来托管自己的代码,今天就来总结下Git的相关用法 ...