这不是一个答案,但我发布这个是因为正确的多线程编码并非易事,我想强调一些您可能需要考虑的事情。
这是我对您的问题的解决方案。
有几点(我希望)感兴趣:
- 线程被封装在组件中
- 组件实现、生命周期和句柄对象按照 1-class-1-job 的原则进行分解
- 在控制组件活动时会考虑线程间排序
- 通过
emit()函数模板控制std::cout的使用互斥。
- 干净关机
按原样提供的代码。在 MacOS 上测试。会有错误。多线程代码的第一剪总是有的。
//
// main.cpp
// so-58625693
//
#include <ciso646>
#include <vector>
#include <iostream>
#include <memory>
#include <string>
#include <sstream>
#include <cassert>
#include <thread>
#include <condition_variable>
#include <chrono>
std::mutex emit_mutex;
template<class...Ts>
void emit(Ts&&...ts)
{
auto lock = std::unique_lock<std::mutex>(emit_mutex);
int x[] = {
0,
(std::cout << ts, 0)...
};
(void) x;
}
// implements the workings of a component
class component_impl : public std::enable_shared_from_this<component_impl>
{
using mutex_type = std::mutex;
using lock_type = std::unique_lock<mutex_type>;
enum state_type
{
stopped,
started,
stopping
};
template<class F>
auto sync(F f) const
{
return f(lock_type(mutex_));
}
public:
component_impl(int type, int id, std::string name)
: type_(type), id_(id), name_(std::move(name))
{}
auto start() -> void
{
sync([&](auto&& lock){
switch(state_)
{
case stopped:
handle_start(lock);
break;
case started:
case stopping:
emit(identity(), " is already started\n");
break;
}
});
}
bool stop() {
auto did_stop = sync([&](auto&& lock){
switch(state_)
{
case stopping:
case stopped:
return false;
case started:
state_ = stopping;
stop_condition_.notify_one();
emit(identity(), " stop requested\n");
return true;
}
return false;
});
if (did_stop and thread_.joinable())
thread_.join();
return did_stop;
}
void display() const
{
identify(std::cout);
std::cout << '\n';
}
void identify(std::ostream& os) const
{
os << type_ << " : " << id_ << " : " << name_;
}
auto identity() const -> std::string
{
std::ostringstream ss;
identify(ss);
return ss.str();
}
auto id() const -> int {
return id_;
}
private:
auto run() -> void
{
using namespace std::literals;
auto lock = lock_type(mutex_);
for(;;)
{
auto stop = stop_condition_.wait_for(lock, 2s, [&] { return state_ != started; });
if (stop)
{
state_ = stopped;
break;
}
else
{
lock.unlock();
emit(identity(), " sampling\n");
lock.lock();
}
}
}
// precondition: mutex is locked
// precondition: not started
auto handle_start(lock_type const& lock) -> void
{
assert(lock.owns_lock());
assert(state_ == stopped);
thread_ = std::thread([self = this->shared_from_this()]
{
self->run();
});
state_ = started;
}
// invariants
const int type_, id_;
const std::string name_;
// control
mutable mutex_type mutex_;
std::condition_variable stop_condition_;
std::thread thread_;
// mutable state
state_type state_ = stopped;
};
struct component_lifetime
{
component_lifetime(std::unique_ptr<component_impl> impl)
: impl_(std::move(impl))
{
}
~component_lifetime()
{
impl_->stop();
}
auto impl() -> component_impl&
{
return *impl_;
}
private:
std::shared_ptr<component_impl> impl_;
};
// manages the lifetime of a component
struct component
{
component(int type, int id, std::string name)
: impl_(construct_lifetime(type, id, std::move(name)))
{
}
component_lifetime& lifetime() { return *impl_; }
component_lifetime& lifetime() const { return *impl_; }
component_impl& impl() { return lifetime().impl(); }
component_impl& impl() const { return lifetime().impl(); }
void start()
{
impl().start();
}
int id() const {
return impl().id();
}
void display() const
{
impl().display();
}
bool stop()
{
return impl().stop();
}
private:
static auto construct_impl(int type, int id, std::string name) -> std::unique_ptr<component_impl>
{
return std::make_unique<component_impl>(type, id, std::move(name));
}
static auto construct_lifetime(int type, int id, std::string name) -> std::shared_ptr<component_lifetime>
{
auto impl = construct_impl(type, id, std::move(name));
auto lifetime = std::make_shared<component_lifetime>(std::move(impl));
return lifetime;
}
std::shared_ptr<component_lifetime> impl_;
};
struct component_set
{
component_set() = default;
component_set(component_set const&) = delete;
component_set& operator=(component_set const&) = delete;
~component_set()
{
shutdown();
}
void add(int type, int id, std::string name) {
auto lock = std::unique_lock<std::mutex>(m_);
v_.emplace_back(type, id, std::move(name));
}
auto locate(int id) -> component*
{
auto match_id = [id](component const& c)
{
return c.id() == id;
};
auto i = std::find_if(std::begin(v_), std::end(v_), match_id);
if (i != std::end(v_))
return std::addressof(*i);
else
return nullptr;
}
void start(int id)
{
auto pc = locate(id);
if (pc)
pc->start();
else
emit("id ", id, " not found\n");
}
void stop(int id)
{
auto pc = locate(id);
if (pc)
{
if (not pc->stop())
{
emit("id ", id, " was not running");
}
}
else
{
emit("id ", id, " not found\n");
}
}
void display()
{
auto lock = std::unique_lock<std::mutex>(m_);
for (auto&& c : v_)
{
c.display();
}
}
auto shutdown() -> void
{
for (auto&& c : v_)
c.stop();
}
private:
std::mutex m_;
std::vector < component > v_;
};
int main() {
int choose;
int type;
int id;
std::string name;
component_set components;
do {
emit("1.ADD A COMPONENT\n"
"2.DISPLAY A COMPONENT\n"
"3.START MONITORING\n"
"4.STOP MONITORING\n"
"5.QUIT\n");
std::cin >> choose;
switch (choose) {
case 1:
emit("enter type, id and name:\n");
std::cin >> type >> id >> name;
components.add(type, id, name);
// fall through
case 2:
components.display();
break;
case 3:
emit("ID to be monitored\n");
std::cin >> id;
components.start(id);
break;
case 4:
emit("ID to be stopped\n");
std::cin >> id;
components.stop(id);
break;
}
} while (choose != 5);
}