我正在尝试编写一个函数TimeoutFunction
,它调用另一个函数e.x. printf
,如果在x秒内未再次调用函数TimeoutFunction
。如果再次调用,则应重置超时。
这段代码例如:
void TimeoutFunction(string MyString)
{
//Wait 5 seconds and then call printf(MyString)
}
int main()
{
TimeoutFunction("A");
Sleep(4);
TimeoutFunction("B");
Sleep(6);
TimeoutFunction("C");
Sleep(10);
TimeoutFunction("D");
Sleep(2);
TimeoutFunction("E");
}
会打印:
BCE
或至少BC
参数MyString
不是必需的,但我添加了它以使其可视化。
答案 0 :(得分:0)
我写了一些不太好的代码,但它可以正常工作
#include <thread>
#include <windows.h>
bool running;
unsigned long StartTime;
unsigned int WaitTime = 500;
const char* PrintString;
std::thread InternThread;
void MyPrint()
{
while (running)
{
auto WaitedTime = GetTickCount() - StartTime;
if (WaitedTime > WaitTime)
{
printf(PrintString);
break;
}
Sleep(10);
}
}
void TimeoutFunction(const char* MyString)
{
StartTime = GetTickCount();
PrintString = MyString;
running = false;
if (InternThread.joinable())
{
InternThread.join();
}
running = true;
InternThread = std::thread(MyPrint);
}
int main()
{
TimeoutFunction("A");
Sleep(400);
TimeoutFunction("B");
Sleep(600);
TimeoutFunction("C");
Sleep(1000);
TimeoutFunction("D");
Sleep(200);
TimeoutFunction("E");
InternThread.join();
return 0;
}
如果某人有更好的代码,欢迎您。
答案 1 :(得分:0)
我根据your self-answer编写了等效的有希望的可移植代码。
但首先:
您在自己的答案中使用的技术是明智的,确保主线程或工作线程随时访问公共共享变量。这不是我第一次想到如何做到这一点,即它对我来说并不明显,它使代码比使用互斥锁确保对这些变量的独占访问更简单,更有效。但是,编码方式存在两个同步问题:
bool
标志running
需要设置为线程安全的
如果该标志不是线程安全的,那么在一个线程(例如主线程)中进行的更改可能只是将其发送到某个缓存,但不是一直到主内存,同样,另一个线程的检查可能只是检查缓存,而不是直接主内存。三个可能性是std::atomic<bool>
,std::atomic_flag
(不太方便,但保证无锁定),第三,使用额外的std::mutex
,例如结合std::unique_lock
。
在TimeoutFunction
函数中,共享状态更新需要在加入线程后进行。
为了完善,GetTickCount
结果应该在等待线程加入之前存储在局部变量中。
对于标准的C ++希望可移植的代码,main
函数就像这样(与你发布的答案几乎完全相同):
#include <stdio.h> // printf
#include <Delayed_action.hpp> // my::(Delayed_action, sleep)
// Alternative to defining lots of small lambdas, a special case functor:
struct Print
{
char const* s;
void operator()() const { printf( "%s", s ); }
Print( char const* const a_literal ): s( a_literal ) {}
};
auto main()
-> int
{
using my::Delayed_action; using my::sleep;
using namespace std::literals; // ms
Delayed_action da;
da.set_action( Print{ "A" } );
sleep( 400ms );
da.set_action( Print{ "B" } );
sleep( 600ms );
da.set_action( Print{ "C" } );
sleep( 1000ms );
da.set_action( Print{ "D" } );
sleep( 200ms );
da.set_action( Print{ "E" } );
da.wait_for_action_completed();
printf( "\n" );
}
这里支持重用的两个主要抽象是
将线程通信状态放在对象而不是全局中。
参数化操作,而不是硬编码操作。
Delayed_action
的实现使用了一些常规支持:
#pragma once
namespace cppx {
class No_copy
{
private:
auto operator=( No_copy const& ) -> No_copy& = delete;
No_copy( No_copy const& ) = delete;
public:
auto operator=( No_copy&& ) -> No_copy& { return *this; }
No_copy() {}
No_copy( No_copy&& ) {}
};
class No_move
{
private:
auto operator=( No_move&& ) -> No_move& = delete;
No_move( No_move&& ) = delete;
public:
auto operator=( No_move const& ) -> No_move& { return *this; }
No_move() {}
No_move( No_move const& ) {}
};
class No_copy_or_move
: public No_copy
, public No_move
{};
} // namespace cppx
CPPX-threading.hpp:的
#pragma once
#include <cppx-class-kinds.hpp> // cppx::No_copy_or_move
#include <atomic> // std::atomic
#include <chrono> // std::chrono::milliseconds
#include <thread> // std::thread
namespace cppx {
namespace this_thread = std::this_thread;
inline namespace std_aliases {
using Milliseconds = std::chrono::milliseconds;
using Steady_clock = std::chrono::steady_clock;
using Time_point = std::chrono::time_point<Steady_clock>;
using Thread = std::thread;
}
inline void sleep( Milliseconds const duration )
{
this_thread::sleep_for( duration );
}
// Syntactic sugar for std::atomic_flag:
// • boolean assignment
// • default init to false.
// std::atomic_flag is guaranteed lock free, as opposed to std::atomic<bool>.
// Cost: there's no way to check the value except by setting it to true.
class Atomic_flag
: public No_copy_or_move
{
private:
std::atomic_flag flag_ = ATOMIC_FLAG_INIT; // Initialized to false.
public:
void clear() { flag_.clear(); }
auto test_and_set() -> bool
{
bool const previous_value = flag_.test_and_set();
return previous_value;
}
void set() { test_and_set(); }
void operator=( bool const should_be_set )
{
if( should_be_set ) set(); else clear();
}
Atomic_flag() {}
};
} // namespace cppx
通过对标准库的事物进行包装和重命名,基于标准库的重新实现您的想法,Delayed_action
类,可以如下所示:
#pragma once
#include <cppx-class-kinds.hpp> // cppx::No_copy_or_move
#include <cppx-threading.hpp> // cppx::(Atomic_flag, sleep)
#include <functional> // std::(function, ref)
#include <utility> // std::move
namespace my {
using namespace cppx::std_aliases;
using namespace std::literals;
using cppx::Atomic_flag;
using cppx::No_copy_or_move;
using cppx::sleep;
using std::move;
using std::ref;
using Action = std::function<void()>;
class Delayed_action
: public No_copy_or_move
{
private:
struct Parameters
{
Atomic_flag run;
Action action;
Time_point when;
};
static void polling( Parameters& parameters )
{
for( ;; )
{
if( not parameters.run.test_and_set() )
{
return;
}
else if( Steady_clock::now() >= parameters.when )
{
parameters.action();
return;
}
sleep( 10ms );
}
}
private:
Parameters parameters_;
Thread worker_;
void join_worker_thread()
{
if( worker_.joinable() )
{
worker_.join();
}
}
void end_worker_thread()
{
parameters_.run = false;
join_worker_thread();
}
public:
static auto default_delay() -> Milliseconds { return 500ms; }
void set_action( Action action, Milliseconds const delay = default_delay() )
{
Time_point const when = Steady_clock::now() + delay;
end_worker_thread();
parameters_.action = move( action );
parameters_.when = when;
parameters_.run = true;
worker_ = Thread( &polling, ref( parameters_ ) );
}
void wait_for_action_completed() { join_worker_thread(); }
~Delayed_action() { end_worker_thread(); }
Delayed_action() {}
Delayed_action( Action action, Milliseconds const delay = default_delay() )
{
set_action( move( action ), delay );
}
};
} // namespace my