Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/125.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
我们在哪里可以在std::闩锁上使用std::屏障? 最近我听到了新的C++标准的特点,即:_C++_Visual C++_Std_C++ Experimental - Fatal编程技术网

我们在哪里可以在std::闩锁上使用std::屏障? 最近我听到了新的C++标准的特点,即:

我们在哪里可以在std::闩锁上使用std::屏障? 最近我听到了新的C++标准的特点,即:,c++,visual-c++,std,c++-experimental,C++,Visual C++,Std,C++ Experimental,我不知道在什么情况下,它们相互适用和有用 如果有人能举一个例子,说明如何明智地使用它们中的每一个,那将非常有帮助 非常简短的回答 他们的目标完全不同: 当您有一组线程并且希望同时跨线程进行同步时,屏障非常有用,例如,要同时对所有线程的数据进行操作 如果您有一堆工作项,并且希望知道它们何时都被处理,并且不一定对哪个线程处理它们感兴趣,那么闩锁非常有用 更长的答案 当您有一个执行某些处理的工作线程池和一个共享的工作项队列时,通常使用屏障和闩锁。这不是使用它们的唯一情况,但这是一种非常常见的情况

我不知道在什么情况下,它们相互适用和有用

  • 如果有人能举一个例子,说明如何明智地使用它们中的每一个,那将非常有帮助
非常简短的回答 他们的目标完全不同:

  • 当您有一组线程并且希望同时跨线程进行同步时,屏障非常有用,例如,要同时对所有线程的数据进行操作
  • 如果您有一堆工作项,并且希望知道它们何时都被处理,并且不一定对哪个线程处理它们感兴趣,那么闩锁非常有用
更长的答案 当您有一个执行某些处理的工作线程池和一个共享的工作项队列时,通常使用屏障和闩锁。这不是使用它们的唯一情况,但这是一种非常常见的情况,并有助于说明它们之间的差异。下面是一些示例代码,可以设置如下线程:

const size_t worker_count = 7; // or whatever
std::vector<std::thread> workers;
std::vector<Proc> procs(worker_count);
Queue<std::function<void(Proc&)>> queue;
for (size_t i = 0; i < worker_count; ++i) {
    workers.push_back(std::thread(
        [p = &procs[i], &queue]() {
            while (auto fn = queue.pop_back()) {
                fn(*p);
            }
        }
    ));
}
工作原理:

  • 线程最终会将工作项弹出队列,可能池中的多个线程同时处理不同的工作项
  • 当每个工作项完成时,将调用
    lack.count\u down()
    ,有效地减少从
    work.size()
    开始的内部计数器
  • 当所有工作项都完成时,该计数器达到零,此时
    lack.wait()
    返回,生产者线程知道所有工作项都已处理
  • 注意事项:

    • 闩锁计数是将要处理的工作项的数量,而不是工作线程的数量
    • count\u down()
      方法可以在每个线程上调用零次、一次或多次,不同线程的调用次数可能不同。例如,即使将7条消息推送到7个线程上,也可能是所有7个项目都被处理到同一个线程上(而不是每个线程一个),这很好
    • 其他不相关的工作项可以与这些工作项交错(例如,因为它们被其他生产者线程推到队列上),这也没关系
    • 原则上,在所有工作线程完成所有工作项的处理之前,
      latch.wait()
      可能不会被调用。(这是编写线程化代码时需要注意的一种奇怪情况。)但没关系,这不是竞争条件:
      latch.wait()
      在这种情况下会立即返回
    • 使用闩锁的另一种选择是,除了此处显示的队列之外,还有另一个队列包含工作项的结果。线程池回调将结果推送到该队列上,而生产者线程将结果从队列中弹出。基本上,它与此代码中的
      队列
      方向相反。这也是一个非常有效的策略,事实上,如果说它更常见的话,但在其他情况下,闩锁更有用
    障碍 屏障通常用于使所有线程同时等待,以便可以同时操作与所有线程关联的数据

    typedef Fn std::function<void()>;
    Fn completionFn = [&procs]() {
        // Do something with the whole vector of Proc objects
    };
    auto barrier = std::make_shared<std::barrier<Fn>>(worker_count, completionFn);
    auto workerFn = [barrier](Proc&) {
        barrier->count_down_and_wait();
    };
    for (size_t i = 0; i < worker_count; ++i) {
        queue.push_back(workerFn);
    }
    
    工作原理:

    关键思想是在每个线程中等待屏障两次,并在其间执行工作。第一次等待的目的与前一个示例相同:它们确保在开始此工作之前完成队列中任何较早的工作项。第二次等待确保队列中的任何后续项在该工作完成之前不会启动

    注意事项:

    注释与前面的屏障示例基本相同,但有一些不同之处:

    • 一个不同之处是,由于屏障没有绑定到特定的完成函数,因此更有可能在多个用途之间共享它,就像我们在闩锁示例中所做的那样,避免使用共享指针
    • 这个例子让人觉得在没有完成函数的情况下使用屏障要复杂得多,但这只是因为这种情况并不适合他们。有时,你所需要的只是到达障碍物。例如,虽然我们在线程启动之前初始化了一个队列,但可能每个线程都有一个队列,但在线程的运行函数中进行了初始化。在这种情况下,屏障可能只是表示队列已经初始化,并准备好让其他线程相互传递消息。在这种情况下,您可以使用没有完成功能的屏障,而不需要像这样等待两次
    • 实际上,您可以为此使用闩锁,调用
      count\u down()
      ,然后调用
      wait()
      来代替
      count\u down\u和\u wait()
      。但是使用屏障更有意义,因为调用组合函数更简单,而且使用屏障可以更好地向代码的未来读者传达您的意图
    • 在任何情况下,以前的“危险”警告仍然适用

    来自链接的
    闩锁
    参考:“不可能增加或重置计数器,从而使闩锁成为一次性屏障。”以及来自链接的
    屏障
    参考:“与std::experimental::latch不同,屏障是可重复使用的;一旦参与的线程从屏障的同步点释放出来,它们就可以重用同一个屏障。“那么你需要重用屏障吗,还是一次性的?@Someprogrammerdude重用屏障…我希望这个链接可以帮助你“[…]在哪些情况下它们是适用和有用的[…]“可能与现在定义的方式不同:
    std::latch
    的问题在于它需要一个显式的同步
    typedef Fn std::function<void()>;
    Fn completionFn = [&procs]() {
        // Do something with the whole vector of Proc objects
    };
    auto barrier = std::make_shared<std::barrier<Fn>>(worker_count, completionFn);
    auto workerFn = [barrier](Proc&) {
        barrier->count_down_and_wait();
    };
    for (size_t i = 0; i < worker_count; ++i) {
        queue.push_back(workerFn);
    }
    
    auto barrier = std::make_shared<std::barrier<>>(worker_count);
    auto workerMainFn = [&procs, barrier](Proc&) {
        barrier->count_down_and_wait();
        // Do something with the whole vector of Proc objects
        barrier->count_down_and_wait();
    };
    auto workerOtherFn = [barrier](Proc&) {
        barrier->count_down_and_wait();  // Wait for work to start
        barrier->count_down_and_wait();  // Wait for work to finish
    }
    queue.push_back(std::move(workerMainFn));
    for (size_t i = 0; i < worker_count - 1; ++i) {
        queue.push_back(workerOtherFn);
    }