C++ 唤醒多个线程以在每个条件下工作一次

C++ 唤醒多个线程以在每个条件下工作一次,c++,multithreading,mutex,condition-variable,C++,Multithreading,Mutex,Condition Variable,我遇到这样一种情况:一个线程需要偶尔唤醒多个工作线程,每个工作线程只需要做一次工作,然后返回睡眠状态等待下一个通知。我使用一个condition_变量来唤醒一切,但我遇到的问题是“仅一次”部分。假设每个线程都很难创建,所以我不想每次都创建并加入它们 // g++ -Wall -o threadtest -pthread threadtest.cpp #include <iostream> #include <condition_variable> #include <

我遇到这样一种情况:一个线程需要偶尔唤醒多个工作线程,每个工作线程只需要做一次工作,然后返回睡眠状态等待下一个通知。我使用一个condition_变量来唤醒一切,但我遇到的问题是“仅一次”部分。假设每个线程都很难创建,所以我不想每次都创建并加入它们

// g++ -Wall -o threadtest -pthread threadtest.cpp
#include <iostream>
#include <condition_variable>
#include <mutex>
#include <thread>
#include <chrono>

std::mutex condMutex;
std::condition_variable condVar;
bool dataReady = false;

void state_change_worker(int id)
{
    while (1)
    {
        {
            std::unique_lock<std::mutex> lck(condMutex);
            condVar.wait(lck, [] { return dataReady; });
            // Do work only once.
            std::cout << "thread " << id << " working\n";
        }
    }
}

int main()
{
    // Create some worker threads.
    std::thread threads[5];
    for (int i = 0; i < 5; ++i)
        threads[i] = std::thread(state_change_worker, i);

    while (1)
    {
        // Signal to the worker threads to work.
        {
            std::cout << "Notifying threads.\n";
            std::unique_lock<std::mutex> lck(condMutex);
            dataReady = true;
            condVar.notify_all();
        }
        // It would be really great if I could wait() on all of the 
        // worker threads being done with their work here, but it's 
        // not strictly necessary.
        std::cout << "Sleep for a bit.\n";
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }
}
//g++-Wall-o threadtest-pthread threadtest.cpp
#包括
#包括
#包括
#包括
#包括
std::mutex condMutex;
std::条件变量condVar;
booldataready=false;
无效状态\u更改\u工作者(int id)
{
而(1)
{
{
std::唯一锁lck(condMutex);
wait(lck,[{returndataready;});
//只做一次工作。
std::cout=目标
计数条件通知一个();
}
//等待领队发出完成的信号。
std::唯一锁定lck(完成互斥);
完成条件等待(lck,[&]{returndone});
{
//递减计数表示我们不再等待。
//如果我们是最后一个设置为false的线程。
std::唯一锁lck(计数互斥);
如果(--计数==0)
{
完成=错误;
}
}
}
void waitForHerd()
{
std::唯一锁lck(计数互斥);
计数条件等待(lck,[&]{返回计数>=target;});
}
void leaderDone()
{
std::唯一锁定lck(完成互斥);
完成=正确;
完成。通知所有人();
}
void incrementTarget()
{
std::唯一锁定lck(目标互斥);
++目标!;
}
无效递减目标()
{
std::唯一锁定lck(目标互斥);
--目标!;
}
无效设置目标(int目标)
{
std::唯一锁定lck(目标互斥);
目标=目标;
}
私人:
//指示引线已完成的条件变量。
std::mutex done\u mutex\u;
std::条件变量完成条件;
bool done=假;
//当前正在等待的任务的计数。
std::互斥体计数\u互斥体\u;
std::条件变量计数条件;
整数计数=0;
//为领导者准备的目标任务数。
std::mutex target\u mutex\u;
int目标=0;
};
壁球队;
std::mutex print_mutex;
无效状态\u更改\u工作者(int id)
{
而(1)
{
//等待领导发出我们准备工作的信号。
班锁。等待队长();
{
//在这里添加一点睡眠可以让每个线程都醒来,但这不是正确的方法。
//std::this_线程::sleep_for(std::chrono::毫秒(100));
std::唯一锁定lck(打印互斥);

STD::CUT

下面是我创建的一个关于并行设计模式的博客的摘录。这些模式是用艾达语言来表达的,但是这些概念是可译的C++。 摘要

许多应用程序是由多组协同执行的线程组成的。历史上,这通常是通过创建一组协同进程来实现的。这些进程将通过共享数据进行协作。起初,只有文件用于共享数据。文件共享会带来一些有趣的问题。如果一个进程被写入当另一个进程读取文件时,您将经常遇到数据损坏,因为读取进程可能会在写入进程完全写入信息之前尝试读取数据。用于此操作的解决方案是创建文件锁,以便一次只能有一个进程打开该文件。Unix管道的概念,它实际上是一个数据队列。一个进程可以写入管道,而另一个进程可以从管道中读取数据。操作系统将管道中的数据视为一系列字节。在写入进程完成对数据的操作之前,它不允许读取进程访问特定字节的数据。 各种操作系统还引入了其他机制,允许进程共享数据。示例包括消息队列、套接字和共享内存。还有一些特殊功能可以帮助程序员控制对数据的访问,例如信号量。当操作系统引入单个进程操作多个线程的能力时执行,也称为轻量级线程,或者只是线程,它们还必须为共享数据提供相应的锁定机制。 经验表明,虽然共享数据的可能设计种类很多,但仍有一些非常常见的设计模式经常出现。具体来说,锁或信号量以及数据缓冲都有一些变化。本文探讨了中线程的锁定和缓冲设计模式监视器的上下文。虽然监视器可以用多种语言实现,但本文中的所有示例都使用Ada保护类型。Ada保护类型是监视器的一个非常彻底的实现

监视器

创建和控制共享内存有几种理论方法。最灵活和最健壮的方法之一是C.A.R.Hoare首先描述的监视器。监视器是具有三种不同操作的数据对象

过程用于更改监视器包含的状态或值。当线程调用监视器过程时,该线程必须以独占方式访问监视器,以防止其他线程遇到损坏或部分写入的数据

条目(如过程)用于更改监视器包含的状态或值,但条目也指定边界条件。只有当边界条件为true时,才能执行该条目。当边界条件为false时调用条目的线程被放置在q中
// g++ -Wall -o threadtest -pthread threadtest.cpp
#include <iostream>
#include <condition_variable>
#include <mutex>
#include <thread>
#include <chrono>

class SquadLock
{
public:
    void waitForLeader()
    {
        {
            // Increment count to show that we are waiting in queue.
            // Also, if we are the thread that reached the target, signal
            // to the leader that everything is ready.
            std::unique_lock<std::mutex> count_lock(count_mutex_);
            std::unique_lock<std::mutex> target_lock(target_mutex_);
            if (++count_ >= target_)
                count_cond_.notify_one();
        }
        // Wait for leader to signal done.
        std::unique_lock<std::mutex> lck(done_mutex_);
        done_cond_.wait(lck, [&] { return done_; });
        {
            // Decrement count to show that we are no longer waiting.
            // If we are the last thread set done to false.
            std::unique_lock<std::mutex> lck(count_mutex_);
            if (--count_ == 0)
            {
                done_ = false;
            }
        }
    }

    void waitForHerd()
    {
        std::unique_lock<std::mutex> lck(count_mutex_);
        count_cond_.wait(lck, [&] { return count_ >= target_; });
    }
    void leaderDone()
    {
        std::unique_lock<std::mutex> lck(done_mutex_);
        done_ = true;
        done_cond_.notify_all();
    }
    void incrementTarget()
    {
        std::unique_lock<std::mutex> lck(target_mutex_);
        ++target_;
    }
    void decrementTarget()
    {
        std::unique_lock<std::mutex> lck(target_mutex_);
        --target_;
    }
    void setTarget(int target)
    {
        std::unique_lock<std::mutex> lck(target_mutex_);
        target_ = target;
    }

private:
    // Condition variable to indicate that the leader is done.
    std::mutex done_mutex_;
    std::condition_variable done_cond_;
    bool done_ = false;

    // Count of currently waiting tasks.
    std::mutex count_mutex_;
    std::condition_variable count_cond_;
    int count_ = 0;

    // Target number of tasks ready for the leader.
    std::mutex target_mutex_;
    int target_ = 0;
};

SquadLock squad_lock;
std::mutex print_mutex;
void state_change_worker(int id)
{
    while (1)
    {
        // Wait for the leader to signal that we are ready to work.
        squad_lock.waitForLeader();
        {
            // Adding just a bit of sleep here makes it so that every thread wakes up, but that isn't the right way.
            // std::this_thread::sleep_for(std::chrono::milliseconds(100));
            std::unique_lock<std::mutex> lck(print_mutex);
            std::cout << "thread " << id << " working\n";
        }
    }
}

int main()
{

    // Create some worker threads and increment target for each one
    // since we want to wait until all threads are finished.
    std::thread threads[5];
    for (int i = 0; i < 5; ++i)
    {
        squad_lock.incrementTarget();
        threads[i] = std::thread(state_change_worker, i);
    }
    while (1)
    {
        // Signal to the worker threads to work.
        std::cout << "Starting threads.\n";
        squad_lock.leaderDone();
        // Wait for the worked threads to be done.
        squad_lock.waitForHerd();
        // Wait until next time, processing results.
        std::cout << "Tasks done, waiting for next time.\n";
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }
}
package Barriers is
   protected type Barrier(Trigger : Positive) is
      entry Wait_For_Leader; 
      entry Wait_For_Herd; 
      procedure Leader_Done; 
   private
      Done : Boolean := False;
   end Barrier;

   protected type Autobarrier(Trigger : Positive) is
      entry Wait_For_Leader; 
      entry Wait_For_Herd; 
   private
      Done : Boolean := False;
   end Autobarrier;
end Barriers;
package body Barriers is
   protected body Barrier is
      entry Wait_For_Herd when Wait_For_Leader'Count >= Trigger is
      begin
         null;
      end Wait_For_Herd;

      entry Wait_For_Leader when Done is
      begin
         if Wait_For_Leader'Count = 0 then
            Done := False;
         end if;
      end Wait_For_Leader;

      procedure Leader_Done is
      begin
         Done := True;
      end Leader_Done;
   end Barrier;

   protected body Autobarrier is
      entry Wait_For_Herd when Wait_For_Leader'Count >= Trigger is
      begin
         Done := True;
      end Wait_For_Herd;

      entry Wait_For_Leader when Done is
      begin
         if Wait_For_Leader'Count = 0 then
            Done := False;
         end if;
      end Wait_For_Leader;
   end Autobarrier;
end Barriers;