我正在尝试为我的C ++代码控制的硬件设备实现同步操作。
假设我可以执行两种类型的设备Open/Close
。
我需要实现的是为Specified Duration
打开一种类型的设备。第二类设备也是如此。
我已用boost::deadline_timer
编写代码:
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/thread.hpp>
#include <boost/asio.hpp>
class Test : public std::enable_shared_from_this <Test>
{
public:
Test() :io_(), timerOne_(io_),timerTwo_(io_){}
void Open(int num);
void Close(int num);
void TimedOpen(int num, int dur);
void Run();
private:
boost::asio::io_service io_;
boost::asio::deadline_timer timerOne_;
boost::asio::deadline_timer timerTwo_;
};
void Test::Open(int type)
{
std::cout << "Open for Number : " << type << std::endl;
}
void Test::Close(int type)
{
std::cout << "Close for Number : " << type << std::endl;
}
void Test::TimedOpen(int type, int dur)
{
switch (type)
{
case 1:
{
io_.reset();
auto fn = std::bind(&Test::Open, shared_from_this(), std::placeholders::_1);
fn(type);
timerOne_.expires_from_now(boost::posix_time::seconds(dur));
timerOne_.async_wait(std::bind(&Test::Close, shared_from_this(), type));
Run();
std::cout << "Function Exiting" << std::endl;
std::cout << "-----------------------------------------------" << std::endl;
return;
}
case 2:
{
io_.reset();
auto fn = std::bind(&Test::Open, shared_from_this(), std::placeholders::_1);
fn(type);
timerTwo_.expires_from_now(boost::posix_time::seconds(dur));
timerTwo_.async_wait(std::bind(&Test::Close, shared_from_this(), type));
Run();
std::cout << "Function Exiting" << std::endl;
std::cout << "-----------------------------------------------" << std::endl;
return;
}
}
}
void Test::Run()
{
boost::thread th(boost::bind(&boost::asio::io_service::run, &io_));
}
int main()
{
auto t = std::make_shared<Test>();
t->TimedOpen(1, 60);
t->TimedOpen(2, 30);
t->TimedOpen(1, 5);
t->TimedOpen(2, 2);
char line[128];
while (std::cin.getline(line, 128))
{
if (strcmp(line, "\n")) break;
}
return 0;
}
输出是:
Open for Number : 1
Function Exiting
-----------------------------------------------
Open for Number : 2
Function Exiting
-----------------------------------------------
Open for Number : 1
Close for Number : 1
Function Exiting
-----------------------------------------------
Open for Number : 2
Close for Number : 2
Function Exiting
-----------------------------------------------
Close for Number : 2
Close for Number : 1
对于timerOne_
它不会等待之前的wait
过期,即只要执行t->TimedOpen(1, 5)
,就会取消之前的操作t->TimedOpen(1, 60)
。
因此,Close for Number : 1
会在输出中显示,而不会等待t->TimedOpen(1, 60)
。
我想要实现的是,如果multiple waits are encountered
适用于任何类型的timer
,则所有操作都应排队,即
如果我输入:
t->TimedOpen(1, 60);
t->TimedOpen(1, 10);
t->TimedOpen(1, 5);
TimedOpen Operation
秒应该60+10+5
。目前它仅持续5秒。它也应该是非阻塞的,即我不能使用wait() instead of async_wait()
。
我如何实现它?
要点:
我的要求是在boost::deadline_timer()
上安排操作,即对其进行多次操作将排队,除非先前的等待时间已过期。
答案 0 :(得分:1)
就像在评论中提到的那样,您将希望每个“类型”都有队列。
让我们将每类型队列命名为“会话”。
通过链接单个strand
¹上单个队列的所有异步等待,您可以获得有效的序列化(还可以避免队列/会话上的同步)。
唯一棘手的问题是在没有飞行时启动异步等待。不变量是异步操作iff !queue_.empty()
:
struct Session : std::enable_shared_from_this<Session> {
Session(boost::asio::io_service &io, int type) : strand_(io), timer_(io), type(type) {}
void Enqueue(int duration) {
auto This = shared_from_this();
strand_.post([This,duration,this] {
std::cout << "t0 + " << std::setw(4) << mark() << "ms Enqueue for Number: " << type << " (dur:" << duration << ")\n";
This->queue_.push(duration);
if (This->queue_.size() == 1)
This->Wait();
});
}
private:
boost::asio::strand strand_;
boost::asio::deadline_timer timer_;
int type;
std::queue<int> queue_;
void Close() {
assert(!queue_.empty());
std::cout << "t0 + " << std::setw(4) << mark() << "ms Close for Number : " << type << " (dur:" << queue_.front() << ") (depth " << queue_.size() << ")\n";
queue_.pop();
Wait();
}
void Wait() {
if (!queue_.empty()) {
std::cout << "t0 + " << std::setw(4) << mark() << "ms Open for Number : " << type << " (dur:" << queue_.front() << ") (depth " << queue_.size() << ")\n";
timer_.expires_from_now(boost::posix_time::milliseconds(queue_.front()));
timer_.async_wait(strand_.wrap(std::bind(&Session::Close, shared_from_this())));
}
}
};
现在Test
类变得更简单了(事实上它根本不需要“共享”,但我已经把这个细节留给了读者的谚语):
class Test : public std::enable_shared_from_this<Test> {
using guard = boost::lock_guard<boost::mutex>;
public:
Test() : io_(), work_(boost::asio::io_service::work(io_)) {
io_thread = boost::thread([this] { io_.run(); });
}
void TimedOpen(int num, int duration);
void Stop() {
{
guard lk(mx_);
if (work_) work_.reset();
}
io_thread.join();
}
~Test() {
Stop();
guard lk(mx_);
timers_ex_.clear();
}
private:
mutable boost::mutex mx_;
boost::asio::io_service io_;
boost::optional<boost::asio::io_service::work> work_;
std::map<int, std::shared_ptr<Session> > timers_ex_;
boost::thread io_thread;
};
void Test::TimedOpen(int type, int duration) {
guard lk(mx_);
auto &session = timers_ex_[type];
if (!session) session = std::make_shared<Session>(io_, type);
session->Enqueue(duration);
}
你可以看到我
t0
io_service
生命周期。现在,施工开始了服务。 work_
变量在空闲时保持活动状态。Stop()
关闭它(首先耗尽会话队列)。Stop()
隐式这是一个测试运行:
<强> Live On Coliru 强>
int main() {
auto t = std::make_shared<Test>();
t->TimedOpen(1, 300);
t->TimedOpen(2, 150);
t->TimedOpen(1, 50);
t->TimedOpen(2, 20);
boost::this_thread::sleep_for(boost::chrono::milliseconds(400));
std::cout << "================\n";
t->TimedOpen(1, 50);
t->TimedOpen(2, 20);
t->TimedOpen(1, 300);
t->TimedOpen(2, 150);
t->Stop();
}
打印
t0 + 0ms Enqueue for Number: 1 (dur:300)
t0 + 0ms Open for Number : 1 (dur:300) (depth 1)
t0 + 0ms Enqueue for Number: 2 (dur:150)
t0 + 0ms Open for Number : 2 (dur:150) (depth 1)
t0 + 0ms Enqueue for Number: 1 (dur:50)
t0 + 0ms Enqueue for Number: 2 (dur:20)
t0 + 150ms Close for Number : 2 (dur:150) (depth 2)
t0 + 150ms Open for Number : 2 (dur:20) (depth 1)
t0 + 170ms Close for Number : 2 (dur:20) (depth 1)
t0 + 300ms Close for Number : 1 (dur:300) (depth 2)
t0 + 300ms Open for Number : 1 (dur:50) (depth 1)
t0 + 350ms Close for Number : 1 (dur:50) (depth 1)
================
t0 + 400ms Enqueue for Number: 1 (dur:50)
t0 + 400ms Open for Number : 1 (dur:50) (depth 1)
t0 + 400ms Enqueue for Number: 2 (dur:20)
t0 + 400ms Open for Number : 2 (dur:20) (depth 1)
t0 + 400ms Enqueue for Number: 1 (dur:300)
t0 + 400ms Enqueue for Number: 2 (dur:150)
t0 + 420ms Close for Number : 2 (dur:20) (depth 2)
t0 + 420ms Open for Number : 2 (dur:150) (depth 1)
t0 + 450ms Close for Number : 1 (dur:50) (depth 2)
t0 + 450ms Open for Number : 1 (dur:300) (depth 1)
t0 + 570ms Close for Number : 2 (dur:150) (depth 1)
t0 + 750ms Close for Number : 1 (dur:300) (depth 1)
¹Why do I need strand per connection when using boost::asio?