C ++-多线程处理需要更长的时间

时间:2019-04-29 21:34:48

标签: c++ multithreading mutex

我正在为任务分配并行密码破解程序。当我启动多个线程时,我添加的线程越多,破解所需的时间就越长。这里有什么问题?

第二,我还可以使用哪些资源共享技术来获得最佳性能?我需要使用互斥体,原子操作或障碍,同时还需要使用信号量,条件变量或通道。互斥体似乎大大降低了我的程序速度。

这是我的上下文代码示例:

std::mutex mtx;
std::condition_variable cv;

void run()
{
  std::unique_lock<std::mutex> lck(mtx);
  ready = true;
  cv.notify_all();
}

crack()
{
  std::lock_guard<std::mutex> lk(mtx);
  ...do cracking stuff
}

main()
{
  ....

  std::thread *t = new std::thread[uiThreadCount];

  for(int i = 0; i < uiThreadCount; i++)
  {
    t[i] = std::thread(crack, params);
  }

  run();

  for(int i = 0; i < uiThreadCount; i++)
  {
    t[i].join();
  }

}

2 个答案:

答案 0 :(得分:1)

编写多线程代码时,通常最好共享尽可能少的资源,因此您可以避免必须使用mutexatomic进行同步。

有很多不同的方法来进行密码破解,因此,我将给出一个更简单的示例。假设您有一个散列函数和一个散列,并且您试图猜测是什么输入产生了散列(这基本上就是破解密码的方式)。

我们可以这样写饼干。它将使用哈希函数和密码哈希,检查值的范围,如果找到匹配项,则调用回调函数。

auto cracker = [](auto passwdHash, auto hashFunc, auto min, auto max, auto callback) {
    for(auto i = min; i < max; i++) {
        auto output = hashFunc(i); 
        if(output == passwdHash) {
             callback(i);
        }
    }
};

现在,我们可以编写一个并行版本。此版本仅在找到匹配项时才需要同步,这非常罕见。

auto parallel_cracker = [](auto passwdHash, auto hashFunc, auto min, auto max, int num_threads) {
    // Get a vector of threads
    std::vector<std::thread> threads;
    threads.reserve(num_threads);

    // Make a vector of all the matches it discovered
    using input_t = decltype(min); 
    std::vector<input_t> matches; 
    std::mutex match_lock;

    // Whenever a match is found, this function gets called
    auto callback = [&](input_t match) {
        std::unique_lock<std::mutex> _lock(match_lock); 
        std::cout << "Found match: " << match << '\n';
        matches.push_back(match); 
    };

    for(int i = 0; i < num_threads; i++) {
        auto sub_min = min + ((max - min) * i) / num_threads;
        auto sub_max = min + ((max - min) * (i + 1)) / num_threads;
        matches.push_back(std::thread(cracker, passwdHash, hashFunc, sub_min, sub_max, callback)); 
    }

    // Join all the threads
    for(auto& thread : threads) {
        thread.join(); 
    }
    return matches; 
};

答案 1 :(得分:0)

是的,它的编写方式不足为奇:将互斥锁放在线程的开头(crack函数),可以有效地使它们按顺序运行

我知道您想实现线程的“同步启动”(通过使用条件变量cv的意图),但是您使用的方式不正确-如果不使用其{{1 }}方法中,调用wait是无用的:它并没有达到您的预期,相反,您的线程只会按顺序运行。

在您的cv.notify_all()调用中使用wait()中的std::condition_variable是必不可少的:它将释放crack()(您刚刚被互斥防护mtx抓住了),并会阻塞线程的执行,直到lk。调用之后,您的其他线程(第一个线程除外,无论哪个线程)都将保留在cv.notify_all()下,因此,如果您确实希望执行“并行”执行,则需要解锁{{1} }。

在这里,您的mtx线程应如下所示:

mtx

顺便说一句,您在crack调用中不需要crack() { std::unique_lock<std::mutex> lk(mtx); cv.wait(lk); lk.unlock(); ...do cracking stuff } 标志-它完全是多余的/未使用的。

  

我必须使用互斥体,原子操作或障碍   同时还使用信号量,条件变量或通道

-不同的工具/技术对不同的事物都有好处,这个问题太笼统了