下面是一些并行递增多个计时器的代码:
main.cpp
using namespace std;
#include <stdio.h>
#include <time.h>
#include <iostream>
#include <math.h>
#include <cstdlib>
#include <unistd.h>
#include <iostream>
#include <sstream>
#include <thread>
#include <vector>
#include <future>
#include "mychrono.hpp"
int main()
{
std::vector<Chronometer*> car_crono;
Chronometer chrono, output_chrono;
std::vector<std::thread> threads;
std::vector<std::future<Chronometer&>> futures;
std::thread th;
//future<Chronometer> ft;
for(int i = 0; i < 2; i++)
{
car_crono.push_back(new Chronometer);
}
while (1) {
for(int i = 0; i<2; i++)
{
//
// //threads.push_back(std::thread(&Chronometer::start_chrono, car_crono[i], std::ref(chrono)));
// auto ft = std::async(std::launch::async, &Chronometer::start_chrono, car_crono[i], std::ref(chrono));
//
// std::cout << "Hello-world" << std::endl;
futures.emplace_back(std::async(std::launch::async, &Chronometer::start_chrono, car_crono[i], std::ref(chrono)));
}
std::cout << "hello-world" << std::endl;
//auto ft = std::async(std::launch::async, &Chronometer::start_chrono, car_crono[0], std::ref(chrono));
//std::cout << "Hello-world-2" << std::endl;
for(auto&& f: futures){
std::cout << f.get() << '\n';
}
}
car_crono.clear();
}mychrono.cpp
#include "mychrono.hpp"
#include <time.h>
#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <sstream>
#include <thread>
//int Chronometer::hour(0), min(0), sec(0);
Chronometer::Chronometer() : hour(0), min(0), sec(0)
{
}
Chronometer& Chronometer::start_chrono(Chronometer& chrono)
{
// if(chrono.hour == 0 && chrono.min == 0 && chrono.sec == 0)
// {
bool condition = true;
while(condition) {
sleep(1);
chrono.sec++;
if(chrono.sec > 59) {
chrono.min++;
chrono.sec = 0;
}
if(chrono.min > 59) {
chrono.hour++;
chrono.sec = 0;
chrono.min = 0;
}
// if(chrono.sec == 10)
// {
// condition = false;
// }
std::cout << "chrono: " << chrono << std::endl;
}
return chrono;
//}
}
Chronometer& Chronometer::finish_chrono(Chronometer& chrono)
{
chrono.hour = 0;
chrono.sec = 0;
chrono.min = 0;
return chrono;
}
std::ostream& operator<<(std::ostream& flux, Chronometer t)
{
flux << t.hour << ":" << t.min << ":" << t.sec;
return flux;
}
Chronometer& Chronometer::operator=(const Chronometer& other)
{
// Guard self assignment
//if (this == &other)
return *this;
}
Chronometer::~Chronometer(){}mychrono.hpp
#include <time.h>
#include <iostream>
#include <sstream>
#ifndef mychrono_hpp
#define mychrono_hpp
class Chronometer
{
private:
int hour, min, sec;
//std::stringstream ss;
//Chronometer chrono;
public:
Chronometer();
Chronometer& start_chrono(Chronometer& chrono);
Chronometer& finish_chrono(Chronometer& chrono);
friend std::ostream& operator<<(std::ostream& flux, Chronometer t);
Chronometer& operator=(const Chronometer& other);
~Chronometer();
};
#endif我的程序运行得很好,我的两个计时器彼此并行,但仍然依赖于我的while循环。例如,在这里,我将打印"hello-world“一次,但需要等待线程停止,以便在while循环中打印第二条"hello-world”消息。
我的问题是,如何让我的线程并行运行,并且完全独立于while循环中的其他指令?
发布于 2021-08-26 12:19:26
Tzig有一个和我类似的想法,那就是使用条件变量等等。我已经制作了一个完整的工作示例,包括注释,希望是为了可读性而编写的。
#include <chrono>
#include <iostream>
#include <iomanip>
#include <mutex>
#include <future>
#include <condition_variable>
//-----------------------------------------------------------------------------------------------------
// state of the timer.
enum class State
{
idle,
starting,
running,
stopping,
stopped
};
//-----------------------------------------------------------------------------------------------------
// helper class for use of std::condition_variable, makes code more readable
// takes into account the pitfalls of condition variables :
// https://www.modernescpp.com/index.php/c-core-guidelines-be-aware-of-the-traps-of-condition-variables
template<typename T>
class StateVariable
{
public:
StateVariable() = delete;
StateVariable(const StateVariable&) = delete;
StateVariable(StateVariable&&) = delete;
StateVariable& operator=(const StateVariable&) = delete;
explicit StateVariable(const T& value) :
m_value{ value }
{
}
void operator=(const T& value) noexcept
{
{
std::unique_lock<std::mutex> lock(m_value_mutex);
m_value = value;
}
m_value_changed.notify_all();
}
// atomic check and set
T set_if(const T& from_value, const T& to_value) noexcept
{
{
std::unique_lock<std::mutex> lock(m_value_mutex);
if (m_value != from_value) return from_value;
m_value = to_value;
}
m_value_changed.notify_all();
return to_value;
}
const bool try_wait_for(const T& value, const std::chrono::steady_clock::duration& duration) const noexcept
{
auto pred = [this, value] { return (m_value == value); };
std::unique_lock<std::mutex> lock(m_value_mutex);
if (pred()) return true;
return m_value_changed.wait_for(lock, duration, pred);
}
void wait_for(const T& value) const
{
try_wait_for(value, std::chrono::steady_clock::duration::max());
}
private:
// mutables so I could make the const promises on wait
// that they wont change the observable state (m_value)
// of this class.
mutable std::mutex m_value_mutex;
mutable std::condition_variable m_value_changed;
std::atomic<T> m_value;
};
//-----------------------------------------------------------------------------------------------------
// helper class for storing elapsed time, helps with readability later on
class ElapsedTime
{
public:
explicit ElapsedTime(const std::chrono::steady_clock::duration& duration) :
m_duration{ duration }
{
}
auto hours() const
{
return std::chrono::duration_cast<std::chrono::hours>(m_duration).count();
}
auto minutes() const
{
return (std::chrono::duration_cast<std::chrono::minutes>(m_duration).count() % 60);
}
auto seconds() const
{
return (std::chrono::duration_cast<std::chrono::seconds>(m_duration).count() % 60);
}
private:
std::chrono::steady_clock::duration m_duration;
};
//-----------------------------------------------------------------------------------------------------
// formatter for ElapsedTime
std::ostream& operator<<(std::ostream& os, const ElapsedTime& t)
{
os << std::setfill('0') << std::setw(2) << t.hours() << ':';
os << std::setfill('0') << std::setw(2) << t.minutes() << ':';
os << std::setfill('0') << std::setw(2) << t.seconds();
return os;
}
//-----------------------------------------------------------------------------------------------------
// ChronoMeter class
// note I use std::chrono classes
class ChronoMeter final
{
public:
ChronoMeter() :
m_state{ State::idle },
m_duration{ std::chrono::steady_clock::duration::min() }
{
};
ChronoMeter(const ChronoMeter&) = delete;
ChronoMeter(ChronoMeter&&) = delete;
ChronoMeter& operator=(const ChronoMeter&) = delete;
void Start()
{
m_start_time = std::chrono::steady_clock::now();
// exercise for the reader, also allow stopped Chronometers to be restarted.
// for now just this simple state model
if (m_state.set_if(State::idle, State::starting) != State::starting)
{
throw std::runtime_error("only an idle ChronoMeter can be started");
}
// it is okay to capture "this" because the destructor of the
// chronometer synchronizes with termination of this thread through the future
m_future = std::async(std::launch::async, [this]
{
// Set indication that the thread has really started.
// this is important because when std::async returns, this thread exists
// but may not have been scheduled yet.
m_state = State::running;
do
{
// assigning a value to m_duration isn't atomic so protect it.
// we might be reading the value on another thread which might
// result in reading an intermediate state.
std::scoped_lock<std::mutex> lock{ m_data_mtx };
m_duration = std::chrono::steady_clock::now() - m_start_time;
// using a statevariable to check for stopping means it can respond
// during the one second delay and stop immediately.
// this is an advantage over using sleep
} while (!m_state.try_wait_for(State::stopping, std::chrono::seconds(1)));
m_state = State::stopped;
});
// Wait for the thread to have really started
// this way we have a clear post condition for start
m_state.wait_for(State::running);
}
void Stop()
{
// only allow a running Chronometer to be stopped.
// in all other states Stop does nothing
if (m_state.set_if(State::running, State::stopping) == State::stopping)
{
// synchronization with stopped state, as set by other thread
m_state.wait_for(State::stopped);
// future get is not really needed for synchronization.
// but if thread threw an exception it's rethrown here
m_future.get();
}
}
~ChronoMeter()
{
// Automatically stop thread if this hasn't already happened.
Stop();
}
const ElapsedTime Elapsed() const
{
std::scoped_lock<std::mutex> lock{ m_data_mtx };
return ElapsedTime{ m_duration };
}
private:
std::future<void> m_future;
StateVariable<State> m_state;
mutable std::mutex m_data_mtx;
std::chrono::steady_clock::time_point m_start_time;
std::chrono::steady_clock::duration m_duration;
};
int main()
{
ChronoMeter meter1;
ChronoMeter meter2;
meter1.Start();
std::this_thread::sleep_for(std::chrono::seconds(5));
auto elapsed_1_1 = meter1.Elapsed();
std::cout << "Meter 1 elapsed time " << elapsed_1_1 << std::endl;
meter2.Start();
std::this_thread::sleep_for(std::chrono::seconds(4));
auto elapsed_1_2 = meter1.Elapsed();
auto elapsed_2 = meter2.Elapsed();
std::cout << "Meter 1 elapsed time " << elapsed_1_2 << std::endl;
std::cout << "Meter 2 elapsed time " << elapsed_2 << std::endl;
meter1.Stop();
// not stopping meter2 (and it's thread) explicitly, this is done safely by destructor if needed
return 0;
}发布于 2021-08-26 09:45:23
我通常通过让多线程对象处理与多线程有关的所有事情来解决这个问题,下面是我在你的例子中解决这个问题的方法(我最终重写了很多东西,所以可能行为并不完全是你想要的,你可以使用我的代码作为起点):
main.cpp:
#include <iostream>
#include <vector>
#include "mychrono.hpp"
int main()
{
std::vector<Chronometer*> car_crono;
for(int i = 0; i < 2; i++)
{
car_crono.push_back(new Chronometer);
}
while (1) {
// std::cout << "hello-world" << std::endl;
Chronometer::Time t = car_crono[0]->get_time();
if(t.sec >= 10)
car_crono[0]->reset_chrono();
std::cout << "Seconds of T0: " << t.sec << std::endl;
std::cout << "T1: " << car_crono[1]->to_string() << std::endl;
}
car_crono.clear();
}mychrono.hpp:
#ifndef mychrono_hpp
#define mychrono_hpp
#include <iostream>
#include <thread>
#include <memory>
#include <condition_variable>
#include <mutex>
#include <atomic>
class Chronometer
{
public:
struct Time {
int hour;
int min;
int sec;
};
Chronometer();
void reset_chrono();
friend std::ostream& operator<<(std::ostream& flux, Chronometer& t);
Chronometer& operator=(const Chronometer& other);
std::string to_string();
Time get_time();
~Chronometer();
private:
Time currentTime;
std::mutex timeMutex;
std::condition_variable conditionVariable;
std::unique_ptr<std::thread> thread;
std::mutex CVMutex;
std::atomic<bool> exitNow;
void thread_function();
};
#endifmychrono.cpp:
#include "mychrono.hpp"
Chronometer::Chronometer() : currentTime.hour(0), currentTime.min(0), currentTime.sec(0)
{
thread.reset(new std::thread(&Chronometer::thread_function, this));
}
void Chronometer::reset_chrono()
{
std::lock_guard<std::mutex> lock(timeMutex);
currentTime.hour = 0;
currentTime.sec = 0;
currentTime.min = 0;
}
std::ostream& operator<<(std::ostream& flux, Chronometer& t)
{
flux << t.to_string();
return flux;
}
Chronometer& Chronometer::operator=(const Chronometer& other)
{
// Guard self assignment
//if (this == &other)
return *this;
}
std::string Chronometer::to_string()
{
std::lock_guard<std::mutex> lock(timeMutex);
return std::to_string(currentTime.hour) + ":" + std::to_string(currentTime.min) + ":" + std::to_string(currentTime.sec);
}
Time Chronometer::get_time()
{
return currentTime;
}
Chronometer::~Chronometer()
{
exitNow = true;
{
std::unique_lock<std::mutex> lock(CVMutex);
lock.unlock();
conditionVariable.notify_all();
}
thread->join();
}
void Chronometer::thread_function()
{
std::unique_lock<std::mutex> waitLock(CVMutex);
while(!exitNow)
{
sec++;
if(currentTime.sec > 59) {
std::lock_guard<std::mutex> lock(timeMutex);
currentTime.min++;
currentTime.sec = 0;
}
if(currentTime.min > 59) {
std::lock_guard<std::mutex> lock(timeMutex);
currentTime.hour++;
currentTime.sec = 0;
currentTime.min = 0;
}
// std::cout << "chrono: " << *this << std::endl; //Not thread safe be careful
conditionVariable.wait_for(waitLock, std::chrono::seconds(1));
}
}编辑:关于你的最新评论:你不需要在它的析构函数中重置计时器,因为数据无论如何都会被销毁。如果希望在计数器运行时重置计数器,则需要从主函数调用Chronometer::reset_chrono()。
对于您的注释的第二部分,我在代码中添加了一个get_time函数(我还添加了一个互斥锁以避免数据竞争,我完全忘记了我写原始答案的时候)。当您想要从main函数中获取时钟的当前时间时,只需调用get_time()并使用它返回的结构来获取所需的信息。
我添加了一个小示例来说明如何使用这两个函数。如你所见,main函数甚至不需要知道线程是什么!
我可能错了,但从你问的问题看,我觉得你可能不习惯多线程是如何工作的。这是一个非常困难的概念,也是我觉得你不能通过经验学习的少数几个概念之一,如果是这样的话,你可能想从this one等专门的网站上了解它。我想我拼凑出你会说法语,here's a really good article (that was never finished apparently) about the theory of it和another one in french, more about the specifics of C++。如果你理解了核心概念,并且很难理解我的代码,我计划对其进行注释,但现在Pepijn做了很好的工作,解释了他们在响应中做了什么。
https://stackoverflow.com/questions/68935775
复制相似问题