網站首頁 編程語言 正文
背景
最近在學《并行程序設計導論》這門課,在做使用Pthreads并行化蒙特卡洛法估計 π \pi π的實驗時遇到了一個問題,使用多線程反而要比單線程要慢很多,輸出如下所示
可以看到,使用一個線程時程序運行只需要2.89031秒,但是使用兩個線程時運行時間竟然達到了9.14698秒。
最終發現了問題所在:每個線程在執行下面的函數時,生成隨機數使用了rand()函數,就是這個函數的使用才導致多線程運行時所需要的時間反而更長
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()的區別
權威的解釋請參考Linux的官方文檔
這里主要說說我個人的理解。
rand()
對于rand():
srand()和rand()配套一起使用,可以認為是進程只生成了一個隨機數生成器,所有的線程共用這個隨機數生成器。每調用一次rand(),rand()都會去修改這個隨機數生成器的一些參數,比如說當前種子的值。對于單線程,這樣是沒有問題的,但是對于多線程而言,這顯然會導致臨界段問題,為了解決該問題,可能會使用互斥量等方法,下面假設多個線程同時使用rand()的時候使用互斥量來解決該臨界段問題。如果rand()調用的次數不多,多線程也問題不大,但是對于上述蒙特卡洛法估計 π \pi π的程序,需要調用很多次rand(),這可能會導致每個線程頻繁地對臨界段進行上鎖和解鎖,而臨界段被上鎖后,其他線程無法完成rand()的調用從而被阻塞,這樣會導致效率十分低下,因此會出現使用多線程反而程序運行更慢的問題。
srand()和rand()配套一起使用,可以認為是進程只生成了一個隨機數生成器,所有的線程共用這個隨機數生成器?這點可以用下面的程序進行驗證,該程序從命令行獲取要生成隨機數的數量以及線程的數量,每個線程負責生成?隨機數的數量/線程的數量?個隨機數,隨機數種子設為0.
#include<stdio.h> #include<pthread.h> #include<stdlib.h> int generate_rand_count; int thread_count; // 被線程執行的函數 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; // 從命令行獲取要生成的隨機數的數量以及這些隨機數由多少個線程來生成 generate_rand_count = strtol(argv[1], NULL, 10); thread_count = strtol(argv[2], NULL, 10); all_threads_id = malloc(sizeof(pthread_t) * thread_count); // 設置隨機數種子 srand(0); // 創建線程 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; }
執行結果如下所示
可以看到,無論使用一個線程生成10個隨機數,還是說使用兩個線程來生成10個隨機數,它們生成的這10個隨機數是一樣的,這也就驗證了?所有的線程共用一個隨機數生成器?的觀點
rand_r()
rand_r()的聲明如下所示
int rand_r(unsigned int *seedp);
每次使用rand_r()的時候需要傳給該函數一個隨機數種子的指針,為了解決蒙特卡洛方法估計 π \pi π中出現的問題,使用如下的代碼:
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); }
每個線程執行函數do_Monte_Carlo_simulation()。
使用上面的代碼后,相當于每個線程生成了它自己獨有的隨機數生成器,這樣就不會有臨界段問題,從而解決了多線程運行時所需要的時間反而更長這個問題,下面為更改后程序的輸出:
總結
原文鏈接:https://blog.csdn.net/weixin_45937291/article/details/121972158
- 上一篇:C語言switch語句詳解_C 語言
- 下一篇:c語言實現學生管理系統詳解_C 語言
相關推薦
- 2023-03-15 Android?Studio格式化(Format)代碼快捷鍵介紹_Android
- 2022-10-09 玩轉Go命令行工具Cobra_Golang
- 2023-03-05 so加載Linker跟NameSpace機制詳解_Android
- 2022-12-24 Golang實現組合模式和裝飾模式實例詳解_Golang
- 2022-05-13 Flutter開發之——getX-GetPage中間件(11)
- 2022-04-09 SpringBoot 集成MyBatis-Plus提示反序列化異常:cannot deseriali
- 2022-05-13 類實例化 對象的內存模型 及 內存占用分析
- 2022-11-17 C語言數據結構不掛科指南之棧&隊列&數組詳解_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支