16. 时间和定时器¶
iFun引擎内部使用monotonic clock,为了便于开发人员作业,也提供wall clock,作为参考。此外,还提供用于定期引发event的timer。各种方式的说明如下。
16.1. Monotonic clock¶
Wall clock的优点是与现实世界的时间单位一致,但当同步服务器时间时,时间值会发生漂移。此时,可能会导致服务器内部未能执行须要在特定时刻处理的作业,或是可能会执行两次。因此,iFun引擎的所有时间单位均使用Monotonic clock,建议游戏服务器开发人员也使用monotonic clock。
Monotonic clock正如其名,是一种时间值单调递增的时间。即,同步服务器的时间后,即使慢1秒,也能稳定返回增加的时间值。monotonic clock功能在最新的OS中均有提供,iFun引擎的monotomic clock使用system中提供的monotonic clock。iFun引擎的Monotomic clock提供微秒单位的精密度。
16.1.1. Monotonic clock 应用方法¶
#include <funapi.h>
void test_function(MonotonicClock::Value old) {
// 현재 시간은 다음처럼 얻습니다.
MonotonicClock::Value t = MonotonicClock::Now();
// 시간 비교는 일반 integer 와 같습니다.
if (old < t) {
// old 는 t 보다는 작네요.
}
// struct timespec 으로의 변경은 다음처럼 합니다.
struc timesepc ts;
MonotonicClock::ToTimespec(t, &ts);
}
using funapi;
void TestFunction(Int64 old)
{
// 현재 시간은 다음처럼 얻습니다.
Int64 t = MonotonicClock.Now;
// 시간 비교는 일반 integer 와 같습니다.
if (old < t) {
// old 는 t 보다는 작네요.
}
}
16.2. Wall clock¶
Monotonic clock在修改服务器时间的情况下也可以稳定使用,但它并不是可以让人读取的状态,所以调试时较为困难。因此,iFun引擎还提供了Wall clock。
iFun引擎中的Wall clock会记忆游戏服务器延迟瞬间的现实clock和Monotonic clock值之间的差异,此后在需要Wall clock时,对反映了相应差异的值作出体现。因此,iFun引擎中的Wall clock也是单调递增的。
所以在修改OS时间时,iFun引擎的Wall clock值和OS的时间值会相异。因此建议在iFun引擎中将Wall clock作为参考使用。
16.2.1. Wall clock 应用方法¶
#include <funapi.h>
void test_function(WallClock::Value old) {
// 현재 시간은 다음처럼 얻습니다.
WallClock::Value t = WallClock::Now();
// 시간 비교는 일반 integer 와 같습니다.
if (old < t) {
// old 는 t 보다는 작네요.
}
// 로그로 쉽게 출력할 수 있습니다.
LOG(INFO) << t;
// Monotonic clock 값으로 변환할 수도 있습니다.
MonotonicClock::Value m = WallClock::ToMonotonicClock(t);
}
using funapi;
public void TestFunction(System.DateTime old)
{
// 현재 시간은 다음처럼 얻습니다.
System.DateTime dt = WallClock.Now;
// 시간 비교는 일반 DateTime 과 같습니다.
if (old < t) {
// old 는 t 보다는 작네요.
}
// Monotonic clock 값으로 변환할 수도 있습니다.
Int64 m = WallClock.ToMonotonicClock (dt);
}
16.2.2. 转换为Timestamp¶
可按如下所示,将Wall clock 转换成整数值timestamp。
// 현재 시간에 대한 timestamp 를 얻거나
int64_t ts = WallClock::GetTimestampInSec();
// WallClock::Value 값을 timestamp 로 변환할 수 있습니다.
WallClock::Value t = WallClock::Now();
ts = WallClock::GetTimestampInSec(t);
// 반대로 timestamp 값을 WallClock::Value 로도 변환할 수 있습니다.
t = WallClock::FromTimestampInSec(ts);
// 밀리초 단위로도 가능합니다.
ts = WallClock::GetTimestampInMsec();
ts = WallClock::GetTimestampInMsec(t);
t = WallClock::FromTimestampInMsec(ts);
// 현재 시간에 대한 timestamp 를 얻거나
Int64 ts = WallClock.GetTimestampInSec ();
// System.DateTime 값을 timestamp 로 변환할 수 있습니다.
System.DateTime dateTime = WallClock.Now;
ts = WallClock.GetTimestampInSec (dateTime);
// 반대로 timestamp 값을 System.DateTime 로도 변환할 수 있습니다.
dateTime = WallClock.FromTimestampInSec (ts);
// 밀리초 단위로도 가능합니다.
ts = WallClock.GetTimestampInMsec ();
ts = WallClock.GetTimestampInMsec (dateTime);
dateTime = WallClock.FromTimestampInMsec (ts);
16.2.3. 时间字符串的表示¶
按如下所示,可将Wall clock转化成字符串形式。
Note
时间字符串通过ISO-extended形式表示。 例) 2016-09-21T03:30:16.787663
// 현재 시간에 대한 문자열을 얻거나
string ts = WallClock::GetTimestring();
// WallClock::Value 값을 문자열로 변환할 수 있습니다.
WallClock::Value t = WallClock::Now();
ts = WallClock::GetTimestring(t);
// 반대로 문자열을 WallClock::Value 로도 변환할 수 있습니다.
if (not WallClock::FromTimestring(ts, &t)) {
// ts 가 올바른 iso-extended time string 이 아니면 실패합니다.
}
// 현재 시간에 대한 문자열을 얻거나
string timestr = WallClock.GetTimestring ();
// System.DateTime 값을 문자열로 변환할 수 있습니다.
System.DateTime t = WallClock.Now;
timestr = WallClock.ToTimestring (t);
Log.Info (timestr);
// 반대로 문자열을 System.DateTime 로도 변환할 수 있습니다.
if (!WallClock.FromTimestring (timestr, out t)) {
// timestr 가 올바른 iso-extended time string 이 아니면 실패합니다.
}
16.3. 定时器¶
游戏服务器有时需要定期处理某项作业,为此,需要一种被称为‘Tick’的重复timer。iFun引擎支持重复定时器和一次性定时器。iFun引擎定时器通过上传定时器expire时调用的处理器的方式来使用。以下是其示例。
16.3.1. 定时器应用方法¶
#include <funapi.h>
extern void handler1(Timer::Id tid, const WallClock::Value &at);
extern void handler2(Timer::Id tid, const WallClock::Value &at);
extern void handler3(Timer::Id tid, const WallClock::Value &at);
void test_function() {
// 특정 시간에 expire 되게 하려면 ExpireAt 을 이용합니다.
// 아래 예는 지금으로부터 500msec 뒤에 expire 하도록 설정합니다
Timer::ExpireAt(WallClock::Now() + boost::posix_time::millisec(500), handler1);
// 상대 시간 뒤에 expire 되게 하려면 ExpireAfter 를 이용합니다.
Timer::ExpireAfter(boost::posix_time::millisec(500), handler2);
// 반복해서 expire 되는 timer 는 interval 과 핸들러를 지정합니다.
// 아래는 500msec 단위로 expire 되는 timer 입니다.
Timer::ExpireRepeatedly(boost::posix_time::millisec(500), handler3);
}
Important
Timer::ExpireAt()
, Timer::ExpireAfter()
, Timer::ExpireRepeatedly()
, Timer::Cancel()
함수들은 의도하지 않은 중복 호출을 방지하기 위하여 오브젝트 롤백을 감지하는 기능이 내장되어 있습니다. 오브젝트 서브시스템의 기능을 이용하신다면 事务 을 참고하세요.
using funapi;
public void TestFunction()
{
// 특정 시간에 expire 되게 하려면 ExpireAt 을 이용합니다.
// 아래 예는 지금으로부터 500msec 뒤에 expire 하도록 설정합니다
Timer.ExpireAt (
WallClock.Now + WallClock.FromMsec(1500), TimerHandler1);
// delegate를 이용해 타이머를 호출 할수도 있습니다.
Timer.Handler timer_handler2 = (
/*UInt64 tid*/ tid,
/*System.DateTime*/ value) => {
Log.Info ("timer_handler_2");
};
// 반복해서 expire 되는 timer 는 interval 과 핸들러를 지정합니다.
// 아래는 500msec 단위로 expire 되는 timer 입니다.
// delegate를 이용해 호출하겠습니다.
Timer.ExpireRepeatedly (WallClock.FromSec (1), timer_handler2);
// 반복해서 expire 되는 timer 는 interval 과 핸들러를 지정합니다.
// 아래는 500msec 단위로 expire 되는 timer 입니다.
Timer.ExpireAfter (WallClock.FromMsec(500), TimerHandler3);
}
public void TimerHandler1(UInt64 tid, DateTime value)
{
Log.Info ("TimerHandler_1");
}
public void TimerHandler3(UInt64 tid, DateTime value)
{
Log.Info ("TimerHandler_3");
}
Important
Timer.ExpireAt()
, Timer.ExpireAfter()
, Timer.ExpireRepeatedly()
, Timer::Cancel()
함수들은 의도하지 않은 중복 호출을 방지하기 위하여 오브젝트 롤백을 감지하는 기능이 내장되어 있습니다. 오브젝트 서브시스템의 기능을 이용하신다면 事务 을 참고하세요.