代码之家  ›  专栏  ›  技术社区  ›  Adrian Albert Koch

为什么我的并行任务管理这么慢?

  •  0
  • Adrian Albert Koch  · 技术社区  · 5 年前

    出于下面解释的原因,我已经开始调查创建和运行线程所需的时间。以我的方式,我发现这个过程需要26毫秒,10个线程,这是远远超过它应该-至少从我的理解。

    简短背景:

    我正在研究一个使用寻路的游戏。在添加了更多实体之后,有必要对流程进行网格划分。

    我希望这个尽可能的可读,所以我创建了一个 并行任务 持有 线 , STD::功能 (应该由胎面执行)a 互斥 保护一些写操作和 布尔完成了 一旦线程完成执行,则设置为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:哦,是的,虽然我们在努力,但我还是不知道谁应该拥有这个互斥锁。(是否有一个全局或每个对象一个…)???

    1 回复  |  直到 5 年前
        1
  •  1
  •   Duck Dodgers    5 年前

    如果您只想测量执行时间,我认为您应该将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);
    

    对你 线程函数 ,让它看起来像这样

    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