3、请修改使用互斥量的π计算程序,使临界区在for循环内。这个版本的性能与原来的忙等待版本相比如何?我们怎样解释它?
时间: 2023-09-12 20:05:46 浏览: 153
### 回答1:
回答如下:
您的第一个问题是关于修改使用互斥量的π计算程序,使临界区在for循环内的性能表现以及解释。修改后的程序将会在每个for循环内部加锁和解锁,以确保每个线程在进入临界区时都是单线程执行的。这将会减少线程等待的时间,并提升程序的性能。与原来的忙等待版本相比,使用互斥量的版本可以充分利用CPU资源,减少CPU占用率。
我们可以解释这个现象为原来的忙等待版本中线程不断尝试获取锁,造成了大量的CPU资源被浪费,而使用互斥量版本中线程只在临界区内运行,不会占用不必要的资源,因此性能更好。
### 回答2:
将临界区放在for循环内,意味着每次迭代都会去获取和释放互斥量锁。修改后的程序如下:
```cpp
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>
std::mutex mtx;
double sum = 0.0;
void calculate_pi(int start, int end)
{
double partial_sum = 0.0;
for (int i = start; i < end; i++)
{
double x = (i + 0.5) / end;
partial_sum += 4.0 / (1.0 + x * x);
}
std::lock_guard<std::mutex> lock(mtx);
sum += partial_sum;
}
int main()
{
int num_threads = 4;
int num_steps = 10000000;
std::vector<std::thread> threads;
for (int i = 0; i < num_threads; i++)
{
int start = i * (num_steps / num_threads);
int end = (i + 1) * (num_steps / num_threads);
threads.emplace_back(calculate_pi, start, end);
}
for (auto& thread : threads)
{
thread.join();
}
double pi = sum / num_steps;
std::cout << "Pi: " << pi << std::endl;
return 0;
}
```
相比于忙等待版本,该版本使用了互斥量来保护临界区,每个线程在访问临界区之前需要先获取互斥量锁,并且在访问结束后释放锁。这样可以保证多个线程之间对sum的访问是线程安全的,避免了数据竞争。
性能方面,修改后的版本与忙等待版本相比,可能稍微慢一些。原因是每次迭代都需要获取和释放互斥量锁,这会引入一定的开销。而忙等待版本则是通过自旋等待来避免了锁操作的开销,可能更加高效。
然而,使用互斥量的程序版本具有更好的可读性和健壮性。互斥量的使用使得程序的多线程部分更加安全和可控,降低了出错的风险。虽然性能稍差一些,但在大多数情况下可接受的性能损失是值得的,因为代码的可维护性更重要。
### 回答3:
在原有使用互斥量的π计算程序中,临界区是在for循环外部,即每个线程在计算π之前都需要获取互斥量锁定临界区,计算完成后释放锁定。现在,我们将临界区放在for循环内部。
修改后的程序如下:
```c++
#include<iostream>
#include<thread>
#include<mutex>
#include<cmath>
std::mutex mtx;
double sum = 0.0;
void calculate_pi(int start, int end)
{
double temp = 0.0;
for(int i = start; i < end; ++i)
{
double x = (i + 0.5) / end;
temp += 4.0 / (1 + x * x);
}
std::lock_guard<std::mutex> lock(mtx); // 锁定临界区
sum += temp;
}
int main()
{
const int THREAD_NUM = 4;
std::thread threads[THREAD_NUM];
int start = 0, end = 100000000;
for(int i = 0; i < THREAD_NUM; ++i)
{
threads[i] = std::thread(calculate_pi, start, end);
start = end;
end += 100000000;
}
for(auto& thread : threads)
{
thread.join();
}
double pi = sum / end;
std::cout << "π ≈ " << pi << std::endl;
return 0;
}
```
修改后的性能与原来的忙等待版本相比会有所提升。原因是在原有版本中,临界区在for循环外部,每个线程获取到互斥量后都需要计算很多次的π,然后再释放互斥量,这种方式导致了互斥量锁定的时间较长。而在这个修改版本中,临界区在for循环内部,每个线程获取到互斥量后只需要计算一小部分的π,然后立即释放互斥量,这样可以更充分地利用多线程并行计算的优势,提高了程序的性能。
需要注意的是,虽然修改后的版本在性能上有所提升,但是互斥量在每次进入临界区时都需要加锁,影响了程序的效率。如果问题规模更大,临界区更小,那么性能的提升可能会更加明显。而如果问题规模较小,临界区较大,那么互斥量的锁定和释放操作可能会给程序带来一定的性能损失。所以,在实际应用中,我们需要根据具体情况选择合适的并发处理方案,以平衡性能和程序的正确性。
阅读全文