網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
背景
最近在學(xué)《并行程序設(shè)計(jì)導(dǎo)論》這門課,在做使用Pthreads并行化蒙特卡洛法估計(jì) π \pi π的實(shí)驗(yàn)時(shí)遇到了一個(gè)問(wèn)題,使用多線程反而要比單線程要慢很多,輸出如下所示
可以看到,使用一個(gè)線程時(shí)程序運(yùn)行只需要2.89031秒,但是使用兩個(gè)線程時(shí)運(yùn)行時(shí)間竟然達(dá)到了9.14698秒。
最終發(fā)現(xiàn)了問(wèn)題所在:每個(gè)線程在執(zhí)行下面的函數(shù)時(shí),生成隨機(jī)數(shù)使用了rand()函數(shù),就是這個(gè)函數(shù)的使用才導(dǎo)致多線程運(yùn)行時(shí)所需要的時(shí)間反而更長(zhǎng)
long long total_times_in_cycle; long long local_number_toss; pthread_mutex_t times_in_cycle_mutex = PTHREAD_MUTEX_INITIALIZER; void* do_Monte_Carlo_simulation(void* my_rank){ double offset = RAND_MAX / (double)2; long long times_in_cycle = 0; long long i; for(i = 0; i < local_number_toss; ++i){ double x = rand() / offset - 1; double y = rand() / offset - 1; if(x*x + y*y < 1){ times_in_cycle++; } } pthread_mutex_lock(×_in_cycle_mutex); total_times_in_cycle += times_in_cycle; pthread_mutex_unlock(×_in_cycle_mutex); }
rand()和rand_r()的區(qū)別
權(quán)威的解釋請(qǐng)參考Linux的官方文檔
這里主要說(shuō)說(shuō)我個(gè)人的理解。
rand()
對(duì)于rand():
srand()和rand()配套一起使用,可以認(rèn)為是進(jìn)程只生成了一個(gè)隨機(jī)數(shù)生成器,所有的線程共用這個(gè)隨機(jī)數(shù)生成器。每調(diào)用一次rand(),rand()都會(huì)去修改這個(gè)隨機(jī)數(shù)生成器的一些參數(shù),比如說(shuō)當(dāng)前種子的值。對(duì)于單線程,這樣是沒(méi)有問(wèn)題的,但是對(duì)于多線程而言,這顯然會(huì)導(dǎo)致臨界段問(wèn)題,為了解決該問(wèn)題,可能會(huì)使用互斥量等方法,下面假設(shè)多個(gè)線程同時(shí)使用rand()的時(shí)候使用互斥量來(lái)解決該臨界段問(wèn)題。如果rand()調(diào)用的次數(shù)不多,多線程也問(wèn)題不大,但是對(duì)于上述蒙特卡洛法估計(jì) π \pi π的程序,需要調(diào)用很多次rand(),這可能會(huì)導(dǎo)致每個(gè)線程頻繁地對(duì)臨界段進(jìn)行上鎖和解鎖,而臨界段被上鎖后,其他線程無(wú)法完成rand()的調(diào)用從而被阻塞,這樣會(huì)導(dǎo)致效率十分低下,因此會(huì)出現(xiàn)使用多線程反而程序運(yùn)行更慢的問(wèn)題。
srand()和rand()配套一起使用,可以認(rèn)為是進(jìn)程只生成了一個(gè)隨機(jī)數(shù)生成器,所有的線程共用這個(gè)隨機(jī)數(shù)生成器?這點(diǎn)可以用下面的程序進(jìn)行驗(yàn)證,該程序從命令行獲取要生成隨機(jī)數(shù)的數(shù)量以及線程的數(shù)量,每個(gè)線程負(fù)責(zé)生成?隨機(jī)數(shù)的數(shù)量/線程的數(shù)量?個(gè)隨機(jī)數(shù),隨機(jī)數(shù)種子設(shè)為0.
#include<stdio.h> #include<pthread.h> #include<stdlib.h> int generate_rand_count; int thread_count; // 被線程執(zhí)行的函數(shù) void* thread_func(void* my_rank){ int i; int local_rand_count = generate_rand_count / thread_count; for(i = 0; i < local_rand_count; ++i){ printf("%d ", rand()); } } int main(int argc, char* argv[]){ pthread_t* all_threads_id; // 從命令行獲取要生成的隨機(jī)數(shù)的數(shù)量以及這些隨機(jī)數(shù)由多少個(gè)線程來(lái)生成 generate_rand_count = strtol(argv[1], NULL, 10); thread_count = strtol(argv[2], NULL, 10); all_threads_id = malloc(sizeof(pthread_t) * thread_count); // 設(shè)置隨機(jī)數(shù)種子 srand(0); // 創(chuàng)建線程 int i; for(i = 0; i < thread_count; ++i){ pthread_create(&all_threads_id[i], NULL, thread_func, (void*) i); } for(i = 0; i < thread_count; ++i){ pthread_join(all_threads_id[i], NULL); } printf("\n"); free(all_threads_id); return 0; }
執(zhí)行結(jié)果如下所示
可以看到,無(wú)論使用一個(gè)線程生成10個(gè)隨機(jī)數(shù),還是說(shuō)使用兩個(gè)線程來(lái)生成10個(gè)隨機(jī)數(shù),它們生成的這10個(gè)隨機(jī)數(shù)是一樣的,這也就驗(yàn)證了?所有的線程共用一個(gè)隨機(jī)數(shù)生成器?的觀點(diǎn)
rand_r()
rand_r()的聲明如下所示
int rand_r(unsigned int *seedp);
每次使用rand_r()的時(shí)候需要傳給該函數(shù)一個(gè)隨機(jī)數(shù)種子的指針,為了解決蒙特卡洛方法估計(jì) π \pi π中出現(xiàn)的問(wèn)題,使用如下的代碼:
long long total_times_in_cycle; long long local_number_toss; pthread_mutex_t times_in_cycle_mutex = PTHREAD_MUTEX_INITIALIZER; void* do_Monte_Carlo_simulation(void* my_rank){ unsigned int local_seed = time(NULL); double offset = RAND_MAX / (double)2; long long times_in_cycle = 0; long long i; for(i = 0; i < local_number_toss; ++i){ double x = rand_r(&local_seed) / offset - 1; double y = rand_r(&local_seed) / offset - 1; if(x*x + y*y < 1){ times_in_cycle++; } } pthread_mutex_lock(×_in_cycle_mutex); total_times_in_cycle += times_in_cycle; pthread_mutex_unlock(×_in_cycle_mutex); }
每個(gè)線程執(zhí)行函數(shù)do_Monte_Carlo_simulation()。
使用上面的代碼后,相當(dāng)于每個(gè)線程生成了它自己獨(dú)有的隨機(jī)數(shù)生成器,這樣就不會(huì)有臨界段問(wèn)題,從而解決了多線程運(yùn)行時(shí)所需要的時(shí)間反而更長(zhǎng)這個(gè)問(wèn)題,下面為更改后程序的輸出:
總結(jié)
原文鏈接:https://blog.csdn.net/weixin_45937291/article/details/121972158
相關(guān)推薦
- 2023-10-14 c/c++--__attribute__ 機(jī)制
- 2022-07-08 一文詳解Python中生成器的原理與使用_python
- 2022-04-04 css:動(dòng)畫 小米官網(wǎng)盒子陰影 心跳動(dòng)畫
- 2022-05-24 調(diào)用無(wú)文檔說(shuō)明的?Web?API過(guò)程描述_相關(guān)技巧
- 2022-12-19 Android硬件解碼組件MediaCodec使用教程_Android
- 2022-05-22 prometheus監(jiān)控nginx的實(shí)現(xiàn)_nginx
- 2022-07-10 四種整型數(shù)據(jù)的表現(xiàn)形式以及常見(jiàn)的轉(zhuǎn)移字符
- 2022-10-30 C#實(shí)現(xiàn)單例模式的6種方法小結(jié)_C#教程
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支