c++ - 为什么我的并行任务管理这么慢?
问题描述
由于下面解释的原因,我开始调查创建和运行线程所需的时间。我这样做的方式是,我发现这个过程需要大约 26 毫秒来处理 10 个线程,这比它应该的要长得多——至少从我的理解来看是这样。
简短的背景:
我正在开发一个使用寻路的游戏。添加更多实体后,有必要使流程并行化。
我希望它尽可能具有可读性,所以我创建了一个ParallelTask 类,它包含一个线程、std::function(应该由踏板执行)、一个互斥锁来保护一些写操作,并且完成了一个bool线程完成执行后设置为 true。
我是多线程的新手,所以我不知道这是否是一个好的开始方法,但我总是很困惑为什么执行需要这么长时间。
我已经编写了下面的代码来隔离问题。
int main()
{
std::map<int, std::unique_ptr<ParallelTask>> parallelTaskDictionary;
auto start = std::chrono::system_clock::now();
for (size_t i = 0; i < 10; i++)
{
parallelTaskDictionary.emplace(i, std::make_unique<ParallelTask>());
parallelTaskDictionary[i]->Execute();
}
auto end = std::chrono::system_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << elapsed.count() << std::endl;
parallelTaskDictionary.clear();
return 0;
}
class ParallelTask
{
public:
ParallelTask();
// Join the treads
~ParallelTask();
public:
inline std::vector<int> GetPath() const { return path; }
void Execute();
private:
std::thread thread;
mutable std::mutex mutex;
std::function<void()> threadFunction;
bool completed;
std::vector<int> path;
};
ParallelTask::ParallelTask()
{
threadFunction = [this]() {
{
std::lock_guard<std::mutex> lock(mutex);
this->completed = true;
}
};
}
ParallelTask::~ParallelTask()
{
if (thread.joinable())
thread.join();
}
void ParallelTask::Execute()
{
this->completed = false;
// Launch the thread
this->thread = std::thread(threadFunction);
}
运行这段代码给了我 25 到 26 毫秒的执行时间。由于这是为了在游戏中使用它当然是不可接受的。
如前所述,我不明白为什么,特别是因为 threadFunction 本身确实会注意到。如果您想知道,我什至删除了互斥锁,它给了我几乎相同的结果,所以这里肯定有其他事情发生。(根据我的研究,创建一个线程不应该超过几微秒,但也许我错了^^)
PS:哦,是的,虽然我们正在这样做,但我仍然不明白谁应该拥有互斥锁。(是否有一个全局或每个对象一个......)???
解决方案
如果您只想测量执行时间,我认为您应该将 now 和 end 语句放在threadFunction
唯一完成工作的地方,如下面的代码所示。
#include <map>
#include <iostream>
#include <memory>
#include <chrono>
#include <vector>
#include <thread>
#include <mutex>
#include <functional>
class ParallelTask
{
public:
ParallelTask();
// Join the treads
~ParallelTask();
public:
inline std::vector<int> GetPath() const { return path; }
void Execute();
private:
std::thread thread;
mutable std::mutex mutex;
std::function<void()> threadFunction;
bool completed;
std::vector<int> path;
};
ParallelTask::ParallelTask()
{
threadFunction = [this]() {
{
auto start = std::chrono::system_clock::now();
std::lock_guard<std::mutex> lock(mutex);
this->completed = true;
auto end = std::chrono::system_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << "elapsed time" << elapsed.count() << std::endl;
}
};
}
ParallelTask::~ParallelTask()
{
if (thread.joinable())
thread.join();
}
void ParallelTask::Execute()
{
this->completed = false;
// Launch the thread
this->thread = std::thread(threadFunction);
}
int main()
{
std::map<int, std::unique_ptr<ParallelTask>> parallelTaskDictionary;
for (size_t i = 0; i < 10; i++)
{
parallelTaskDictionary.emplace(i, std::make_unique<ParallelTask>());
parallelTaskDictionary[i]->Execute();
}
parallelTaskDictionary.clear();
return 0;
}
这给出了一个输出:
elapsed time1
elapsed time0
elapsed time0
elapsed time0
elapsed time0
elapsed time0elapsed time
0
elapsed time0
elapsed time0
elapsed time0
因为我们排除了启动线程所需的时间。
作为一个健全的检查,如果你真的想看看实际工作的效果,你可以添加,
using namespace std::chrono_literals;
std::this_thread::sleep_for(2s);
给你的threadFunction
,让它看起来像这样
ParallelTask::ParallelTask()
{
threadFunction = [this]() {
{
auto start = std::chrono::system_clock::now();
std::lock_guard<std::mutex> lock(mutex);
this->completed = true;
using namespace std::chrono_literals;
std::this_thread::sleep_for(2s);
auto end = std::chrono::system_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << "elapsed time" << elapsed.count() << std::endl;
}
};
}
输出将是,
elapsed time2000061
elapsed timeelapsed time2000103
elapsed timeelapsed time20000222000061
elapsed time2000050
2000072
elapsed time2000061
elapsed time200012
推荐阅读
- java - 即使使用 -keep,kotlin 类的构造函数中的参数名称也会被 proguard 删除
- spring-boot - Spring boot actuator:当其他执行器的端点受到保护时,健康端点未显示详细信息
- javascript - 如何用javascript输入测量圆的表面?
- javascript - 从左到右移动画布 - 从右到左,改变颜色和大小
- c - 如何在内联汇编中使用变量偏移量?
- python - 将 numpy 嵌入到 zipapp 存档中
- python - 如何处理熊猫中小块的数据?
- r - apply - 在移动行之前测试多个条件
- java - 将 JsonFile 读取到 ArrayList
- nativescript - 用于 Nativescript-Vue 的搜索栏的 DismissSoftInput()