[英]Find all primes in a range between two numbers using a given amount of threads
問題是使用給定數量的線程來計算兩個數之間的所有素數。 線程應該做的工作量是均勻的。
我對 C++ 和線程都很陌生,我在讓線程首先找到素數,其次是做偶數工作時遇到了一些問題。
當我只用一個線程運行 find_prime function 時,它正在工作,但是在使用多個線程時,我得到了堆緩沖區溢出或我不太明白的東西。 我的方法可能是非常錯誤的,但我的想法是每個線程都應該同時開始搜索素數,如果他們找到了,他們應該檢查另一個線程是否已經找到它。 如果不是,則將素數添加到素數向量中。
錯誤消息很長,但摘要說:
SUMMARY: AddressSanitizer: heap-buffer-overflow (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x43c58) in __asan_memcpy
Shadow bytes around the buggy address:
0x1c04000003b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x1c04000003c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x1c04000003d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x1c04000003e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x1c04000003f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x1c0400000400:[fa]fa 04 fa fa fa fa fa fa fa fa fa fa fa fa fa
0x1c0400000410: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x1c0400000420: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x1c0400000430: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x1c0400000440: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x1c0400000450: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
#include <iostream>
#include <thread>
#include <vector>
using namespace std;
void find_prime(int low, int high, vector<int> &primes) {
bool is_prime = true;
int i, j;
for (i = low; i <= high; i++) {
if (i == 1 || i == 0)
continue;
is_prime = true;
for (j = 2; j <= i / 2; ++j) {
if (i % j == 0) {
is_prime = false;
break;
}
}
if (is_prime && (std::find(primes.begin(), primes.end(), i) != primes.end()))
continue;
if (is_prime) {
primes.push_back(i);
}
}
}
void print(std::vector<int> const &primes) {
for (std::vector<int>::size_type i = 0; i < primes.size(); i++) {
std::cout << primes.at(i) << ' ';
}
}
int main() {
int low = 0, high = 100, num_threads = 4, i;
std::vector<int> primes;
std::vector<thread> threads;
for (i = 0; i < num_threads; i++) {
std::thread t([&](int low, int high) {
find_prime(low, high, primes);
},
low, high);
threads.push_back(std::move(t));
}
for (auto &thread : threads)
thread.join();
print(primes);
cout << "\nSize: " << primes.size();
}
您在示例中所做的基本上是對所有線程進行相同的工作,這不是很有效:)
根據您的問題,我假設您不是在這里尋找最有效的解決方案,而是想熟悉 c++ 中的多線程。 我推薦的一般方法是將原始范圍拆分為專有范圍(例如,對於 2 個線程,將范圍 [0, 99] 拆分為 [0, 49] 和 [50, 99]),在每個范圍內找到素數在不同的線程上,將結果返回到主線程並在那里組合結果。
為了做到這一點,我建議熟悉futures
和promises
,以及能夠從衍生線程返回一些東西。 這是我剛剛發現的一個站點,它解釋了它們以及 c++ 中的其他多線程概念。
上面的代碼有幾個問題。
if (is_prime && (std::find(primes.begin(), primes.end(), i) != primes.end()))
continue;
在您的代碼中,檢查是否已經計算了一個數字。 那么你不會存儲它。 這使它變得更慢。
std::vector
。 寫入std::vector
不是線程安全的。 您需要添加std::mutex
es 或其他同步機制來防止該問題。std::promise
和std::future
與您的線程交換數據。現在稍微解釋一下相等范圍的計算。 將一個范圍分成相等的部分很容易。 我們計算低和高之間的增量,然后將 integer 除以所需組的數量。 這可以在有或沒有余數的情況下工作。 我們可以通過模除法得到余數。
然后我們會得到類似的東西
const size_t delta = high+1 - low;
const size_t chunk = delta / numberOfGroups;
size_t remainder = delta % numberOfGroups;
請注意:余數當然總是小於組數。 例如,如果您對 4 進行模除,則余數只能是 0,1,2,3。 再也不會了。 如果我們有剩余,那么我們將其平均分配給塊。 因此,我們可以編寫一個非常簡單的 function,如下所示:
// Calculate start and end index for all chunks
std::vector<std::pair<size_t, size_t>> calculatePairs(const size_t low, const size_t high, const size_t numberOfGroups) {
// Here we will store the resulting pairs with start and end values
std::vector<std::pair<size_t, size_t>> pairs{};
// Calculate chung size and remainder
const size_t delta = high+1 - low;
const size_t chunk = delta / numberOfGroups;
size_t remainder = delta % numberOfGroups;
// Calculate the chunks start and end addresses for all chunks
size_t startIndex{}, endIndex{};
for (size_t i = 0; i < numberOfGroups; ++i) {
// Calculate end address and distribute remainder equally
endIndex = startIndex + chunk + (remainder ? 1 : 0) - 1;
// Store a new pair of start and end indices
pairs.emplace_back(startIndex, endIndex);
// Nexct start index
startIndex = endIndex + 1;
// We now consumed 1 remainder
if (remainder) --remainder;
}
return pairs;
}
這將為每個請求的組計算帶有開始結束索引的std::pair
。 在行
endIndex = startIndex + chunk + (remainder ? 1 : 0) - 1;
我們進行余數的分配。 如果有余數,則將當前結束索引加 1。 然后我們從余數中減去 1,因為我們只是從中分配了 1。
好的。 現在我們有了這些對。 這些高低指數對可以提供給我們的線程素數計算器。
下一個。 std::thread
和/或主程序之間的數據交換:為此 C++ 提供所謂的std::promise
和std::future
。 很多人很難理解這些。 請同時檢查 cppreference。
很簡單的說:
std::promise
是一個“事物”,您 promise 表示線程將來會返回一個“值”。 您的線程將使用其 ``set``` 函數將值存儲在std::promise
中。future
,您可以使用std::future
的get
function 檢索已存儲在promise
中的值。 它也會等待,直到數據可用。這非常簡化。 但也許它有助於理解機制。
請在下面查看您修改后的代碼
#include <iostream>
#include <thread>
#include <vector>
#include <future>
#include <utility>
// Calculate start and end index for all chunks
std::vector<std::pair<size_t, size_t>> calculatePairs(const size_t low, const size_t high, const size_t numberOfGroups) {
// Here we will store the resulting pairs with start and end values
std::vector<std::pair<size_t, size_t>> pairs{};
// Calculate chung size and remainder
const size_t delta = high+1 - low;
const size_t chunk = delta / numberOfGroups;
size_t remainder = delta % numberOfGroups;
// Calculate the chunks start and end addresses for all chunks
size_t startIndex{}, endIndex{};
for (size_t i = 0; i < numberOfGroups; ++i) {
// Calculate end address and distribute remainder equally
endIndex = startIndex + chunk + (remainder ? 1 : 0) - 1;
// Store a new pair of start and end indices
pairs.emplace_back(startIndex, endIndex);
// Nexct start index
startIndex = endIndex + 1;
// We now consumed 1 remainder
if (remainder) --remainder;
}
return pairs;
}
void find_prime(const size_t low, const size_t high, std::promise<std::vector<int>>&& promisePrimes) {
bool is_prime = true;
std::vector<int> primes{};
for (size_t i = low; i <= high; i++) {
if (i == 1 || i == 0)
continue;
is_prime = true;
for (size_t j = 2; j <= i / 2; ++j) {
if (i % j == 0) {
is_prime = false;
break;
}
}
if (is_prime) {
primes.push_back(i);
}
}
promisePrimes.set_value(std::move(primes));
}
void print(std::vector<int> const& primes) {
for (std::vector<int>::size_type i = 0; i < primes.size(); i++) {
std::cout << primes.at(i) << ' ';
}
}
int main() {
constexpr size_t low = 0, high = 100, num_threads = 4;
std::vector<int> primes;
std::vector<std::thread> threads;
std::vector<std::pair<size_t, size_t>> pairs = calculatePairs(low, high, num_threads);
std::vector<std::promise<std::vector<int>>> promisePrimes(num_threads);
std::vector<std::shared_future<std::vector<int>>> futurePrimes(num_threads);
for (size_t i = 0; i < num_threads; i++) {
futurePrimes[i] = promisePrimes[i].get_future();
std::thread t(find_prime, pairs[i].first, pairs[i].second, std::move(promisePrimes[i]));
threads.push_back(std::move(t));
}
for (size_t i = 0; i < num_threads; i++) {
std::vector<int> temp = futurePrimes[i].get();
primes.insert(primes.end(), temp.begin(), temp.end());
threads[i].join();
}
print(primes);
std::cout << "\nSize: " << primes.size();
return 0;
}
請注意:我使用std::shared_future
,因為我想將其存儲在std::vector
中,並且無法復制“正常” std::future
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.