我是新来的线程,我想测试我新获得的技能,通过一个简单的任务,使用创建图像多线程,有趣的部分是,在单线程上,程序比使用 4 个线程运行得更快(我相信这是我最高效的并行线程运行能力)我有一个 i3 处理器,使用 ubuntu 17,我的std::thread::hardware_concurrency 为 4。我的代码:
#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
#include <png++/png.hpp>
#include <time.h>
std::vector<int> bounds(int max, int parts)
{
std::vector<int> interval;
int gap = max / parts;
int left = max % parts;
int nr1 = 0;
int nr2;
interval.push_back(nr1);
for (int i = 0; i < parts; i++)
{
nr2 = nr1 + gap;
if (i == parts - 1)
nr2 += left;
nr1 = nr2;
interval.push_back(nr2);
}
return interval;
}
void create_image(png::image<png::rgb_pixel> &image, int start, int end)
{
std::mutex my_mutex;
std::lock_guard<std::mutex> locker(my_mutex);
srand(time(NULL));
for (int i = start; i < end; i++)
for (int j = 0; j < image.get_height(); j++)
image[i][j] = png::rgb_pixel(rand() % 256, 0, rand() % 256);
}
int main()
{
png::image<png::rgb_pixel> png_image(6000, 6000); //Creating Image
int parts = 1; //amount of parallel threads
std::vector<int> my_vector = bounds(png_image.get_width(), parts); //interval vector
std::vector<std::thread> workers; //threads
time_t start, end;
time(&start); //measuring time
for (int i = 0; i < parts - 1; i++)
{
workers.push_back(std::thread(create_image, std::ref(png_image), my_vector[i], my_vector[i + 1]));
}
for (int i = 0; i < parts - 1; i++)
workers[i].join();
create_image(png_image, my_vector[parts - 1], my_vector[parts]);
png_image.write("test.png");
time(&end);
std::cout << (end - start) << " seconds\n";
return 0;
}
要构建它,请运行g++ file.cpp -o test -lpng -pthread
(使用PNG++)。
答案1
互斥锁是一个转移注意力的东西——它是函数本地的,因此它实际上并没有锁定任何东西,因为每个线程最终都有一个单独的互斥锁。为了实际锁定,您需要将互斥变量移出 create_image。
但是,对映像的写入是独立的,因此实际上不需要锁定。也就是说,由于每次调用 create_image 都是针对一个单独的区域,因此写入不会重叠。您可以通过加入线程等待其完成来保证更改将被记录。
问题实际上是rand()。根据我的测试,它有自己的内部互斥锁,这导致了所有的速度减慢。从 rand() 更改为 rand_r(&seed) 会产生很大的差异。使用的线程越多,锁定(每次调用)的成本就越高,因此您会看到速度减慢。
话虽如此,在我的 CPU 上,PNG 的创建是该程序的主要成本。在不写入 PNG 图像的情况下,程序运行时间不到 2 秒(单线程),并且与所使用的核心数量几乎呈线性扩展。写入 PNG 图像时,时间会跳至超过 8 秒,因此写入 PNG 图像比创建图像花费的时间要长得多。
这是我想出的:
#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
#include <png++/png.hpp>
#include <time.h>
std::vector<int> bounds(int max, int parts)
{
std::vector<int> interval;
int gap = max / parts;
int left = max % parts;
int nr1 = 0;
int nr2;
interval.push_back(nr1);
for (int i = 0; i < parts; i++)
{
nr2 = nr1 + gap;
if (i == parts - 1)
nr2 += left;
nr1 = nr2;
interval.push_back(nr2);
}
return interval;
}
void create_image(png::image<png::rgb_pixel> &image, int start, int end)
{
unsigned int seed = time(NULL);
for (int i = start; i < end; i++)
for (int j = 0; j < image.get_height(); j++)
image[i][j] = png::rgb_pixel(rand_r(&seed) % 256, 0, rand_r(&seed) % 256);
}
int main()
{
png::image<png::rgb_pixel> png_image(6000, 6000); //Creating Image
int parts = 1; //amount of parallel threads
std::vector<int> my_vector = bounds(png_image.get_width(), parts); //interval vector
std::vector<std::thread> workers; //threads
time_t start, end;
time(&start); //measuring time
for (int i = 0; i < parts; i++)
{
workers.push_back(std::thread(create_image, std::ref(png_image), my_vector[i], my_vector[i + 1]));
}
for (int i = 0; i < parts; i++)
workers[i].join();
png_image.write("test.png");
time(&end);
std::cout << (end - start) << " seconds\n";
return 0;
}