在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

定時器的實現(xiàn)數(shù)據(jù)結(jié)構(gòu)選擇

科技綠洲 ? 來源:Linux開發(fā)架構(gòu)之路 ? 作者:Linux開發(fā)架構(gòu)之路 ? 2023-11-13 14:22 ? 次閱讀

在后端的開發(fā)中,定時器有很廣泛的應(yīng)用。

比如:

心跳檢測

倒計時

游戲開發(fā)的技能冷卻

redis的鍵值的有效期等等,都會使用到定時器。

定時器的實現(xiàn)數(shù)據(jù)結(jié)構(gòu)選擇

紅黑樹

對于增刪查,時間復(fù)雜度為O(logn),對于紅黑樹最?節(jié)點為最左側(cè)節(jié)點,時間復(fù)雜度O(logn)

最小堆

對于增查,時間復(fù)雜度為O(logn),對于刪時間復(fù)雜度為O(n),但是可以通過輔助數(shù)據(jù)結(jié)構(gòu)( map 或者hashtable來快速索引節(jié)點)來加快刪除操作;對于最?節(jié)點為根節(jié)點,時間復(fù)雜度為O(1)

跳表

對于增刪查,時間復(fù)雜度為O(logn),對于跳表最?節(jié)點為最左側(cè)節(jié)點,時間復(fù)雜度為O(1),但是空間復(fù)雜度?較?,為O(1.5n)

時間輪

對于增刪查,時間復(fù)雜度為O(1),查找最?節(jié)點也為O(1)

libco的使用了時間輪的實現(xiàn)

首先,時間輪有幾個結(jié)構(gòu),必須理清他們的關(guān)系。

struct stTimeoutItem_t
{
	enum { eMaxTimeout = 40 * 1000 };	// 40s
	stTimeoutItem_t* pPrev;				// 前
	stTimeoutItem_t* pNext;				// 后
	stTimeoutItemLink_t* pLink;			// 鏈表,沒有用到,寫這里有毛用

	OnPreparePfn_t pfnPrepare;			// 不是超時的事件的處理函數(shù)
	OnProcessPfn_t pfnProcess;			// resume協(xié)程回調(diào)函數(shù)

	void* pArg;							// routine 協(xié)程對象指針
	bool bTimeout;						// 是否超時
	unsigned long long ullExpireTime;	// 到期時間
};

struct stPoll_t;
struct stPollItem_t : public stTimeoutItem_t
{
	struct pollfd* pSelf;			// 對應(yīng)的poll結(jié)構(gòu)
	stPoll_t* pPoll;				// 所屬的stPoll_t
	struct epoll_event stEvent;		// epoll事件,poll轉(zhuǎn)換過來的
};

// co_poll_inner 創(chuàng)建,管理這多個stPollItem_t
struct stPoll_t : public stTimeoutItem_t
{
	struct pollfd* fds;				// poll 的fd集合
	nfds_t nfds;					// poll 事件個數(shù)
	stPollItem_t* pPollItems;		// 要加入epoll 事件
	int iAllEventDetach;			// 如果處理過該對象的子項目pPollItems,賦值為1
	int iEpollFd;					// epoll fd句柄
	int iRaiseCnt;					// 此次觸發(fā)的事件數(shù)
};

我把這幾個結(jié)構(gòu)拉一起了,

圖片

其中,能看出,stCoEpool_t管理了這一切

// TimeoutItem的鏈表
struct stTimeoutItemLink_t
{
	stTimeoutItem_t* head;
	stTimeoutItem_t* tail;
};

// TimeOut 
struct stTimeout_t	// 時間倫
{
	stTimeoutItemLink_t* pItems;	// 時間輪鏈表,開始初始化分配只一圈的長度,后續(xù)直接使用
	int iItemSize;					// 超時鏈表中一圈的tick 60*1000
	unsigned long long ullStart;	// 時間輪開始時間,會一直變化
	long long llStartIdx;			// 時間輪開始的下標(biāo),會一直變化
};

// epoll 結(jié)構(gòu)
struct stCoEpoll_t
{
	int iEpollFd;
	static const int _EPOLL_SIZE = 1024 * 10;
	struct stTimeout_t* pTimeout;					// epoll 存著時間輪
	struct stTimeoutItemLink_t* pstTimeoutList;		// 超時事件鏈表
	struct stTimeoutItemLink_t* pstActiveList;		// 用于signal時會插入
	co_epoll_res* result;
};

也就是說,一個協(xié)程,就有一個,在co_init_curr_thread_env 中創(chuàng)建

它管理著超時鏈表,信號事件鏈表

其中的pTimeout,就是時間輪,也就是一個數(shù)組,這個數(shù)組的大小位60*1000

圖片

stTimeout_t *AllocTimeout( int iSize )
{
	stTimeout_t *lp = (stTimeout_t*)calloc( 1,sizeof(stTimeout_t) );
	lp- >iItemSize = iSize;
	// 注意這里先把item分配好了,后續(xù)直接使用
	lp- >pItems = (stTimeoutItemLink_t*)calloc( 1, sizeof(stTimeoutItemLink_t) * lp- >iItemSize );
	lp- >ullStart = GetTickMS();
	lp- >llStartIdx = 0;
	return lp;
}

這就是分配的時間輪的方法,首先指定了下標(biāo)時間等信息,根據(jù)結(jié)構(gòu)注釋應(yīng)該不難懂

// apTimeout:時間輪
// apItem: 某一個定時item
// allNow:當(dāng)前的時間
// 函數(shù)目的,將超時項apItem加入到apTimeout
int AddTimeout( stTimeout_t *apTimeout, stTimeoutItem_t *apItem ,unsigned long long allNow )
{
// 這個判斷有點多余,start正常已經(jīng)分配了
if( apTimeout->ullStart == 0 )
{
apTimeout->ullStart = allNow;
apTimeout->llStartIdx = 0;
}
// 當(dāng)前時間也不大可能比前面的時間大
if( allNow < apTimeout->ullStart )
{
co_log_err("CO_ERR: AddTimeout line %d allNow %llu apTimeout->ullStart %llu",
LINE ,allNow,apTimeout->ullStart);

return __LINE__;
}
if( apItem- >ullExpireTime < allNow )
{
	co_log_err("CO_ERR: AddTimeout line %d apItem- >ullExpireTime %llu allNow %llu apTimeout- >ullStart %llu",
				__LINE__,apItem- >ullExpireTime,allNow,apTimeout- >ullStart);

	return __LINE__;
}
// 到期時間到start的時間差
unsigned long long diff = apItem- >ullExpireTime - apTimeout- >ullStart;
// itemsize 實際上是毫秒數(shù),如果超出了,說明設(shè)置的超時時間過長
if( diff >= (unsigned long long)apTimeout- >iItemSize )
{
	diff = apTimeout- >iItemSize - 1;
	co_log_err("CO_ERR: AddTimeout line %d diff %d",
				__LINE__,diff);

	//return __LINE__;
}
// 將apItem加到末尾
AddTail( apTimeout- >pItems + ( apTimeout- >llStartIdx + diff ) % apTimeout- >iItemSize , apItem );

return 0;

}

其實,這里有個概念,stTimeoutItemLink_tstTimeoutItem_t,也就是說,stTimeout_t里面管理的時60*1000個鏈表,而每個鏈表有一個或者多個stTimeoutItem_t,下面這個函數(shù),就是把節(jié)點Item加入到鏈表的方法。

template
void inline AddTail(TLink*apLink, TNode ap)
{
if( ap->pLink )
{
return ;
}
if(apLink->tail)
{
apLink->tail->pNext = (TNode
)ap;
ap->pNext = NULL;
ap->pPrev = apLink->tail;
apLink->tail = ap;
}
else
{
apLink->head = apLink->tail = ap;
ap->pNext = ap->pPrev = NULL;
}
ap->pLink = apLink;
}

![圖片](//file1.elecfans.com/web2/M00/AD/ED/wKgaomVRwIaAdU0AAADdUXMLTLQ483.jpg)

到這里,基本把一個超時事件添加到時間輪中了,這時就應(yīng)該切換協(xié)程了co_yield_env

int ret = AddTimeout( ctx->pTimeout, &arg, now );
int iRaiseCnt = 0;
if( ret != 0 )
{
co_log_err("CO_ERR: AddTimeout ret %d now %lld timeout %d arg.ullExpireTime %lld",
ret,now,timeout,arg.ullExpireTime);
errno = EINVAL;
iRaiseCnt = -1;
}
else
{
co_yield_env( co_get_curr_thread_env() );
iRaiseCnt = arg.iRaiseCnt;
}

接下來,看怎么檢測超時事件co_eventloop

for(;;)
{
// 等待事件或超時1ms
int ret = co_epoll_wait( ctx->iEpollFd,result,stCoEpoll_t::_EPOLL_SIZE, 1 );

//  遍歷所有ret事件處理
	for(int i=0;i< ret;i++)
	{
		pfnPrepare(xxx)
	}

	// 取出所有的超時時間item,設(shè)置為超時
	TakeAllTimeout( ctx- >pTimeout, now, plsTimeout );
	stTimeoutItem_t *lp = plsTimeout- >head;
	while( lp )
	{
		lp- >bTimeout = true;
		lp = lp- >pNext;
	}

	// 將超時鏈表plsTimeout加入到plsActive
	Join< stTimeoutItem_t, stTimeoutItemLink_t >( plsActive, plsTimeout );
	lp = plsActive- >head;
	while( lp )
	{
        // 彈出鏈表頭,處理超時事件
		PopHead< stTimeoutItem_t,stTimeoutItemLink_t >( plsActive );
        if (lp- >bTimeout && now < lp- >ullExpireTime) 
		{
			int ret = AddTimeout(ctx- >pTimeout, lp, now);
			if (!ret) 
			{
				lp- >bTimeout = false;
				lp = plsActive- >head;
				continue;
			}
		}
        // 只有stPool_t 才需要切協(xié)程,要切回去了
		if( lp- >pfnProcess )
		{
			lp- >pfnProcess( lp );
		}
		lp = plsActive- >head;
	}

	// 如果傳入該函數(shù)指針,則可以控制event_loop 退出
	if( pfn )
	{
		if( -1 == pfn( arg ) )
		{
			break;
		}
	}
}
其中包括了定時事件處理,協(xié)程切換,主協(xié)程退出等操作。如果設(shè)置了主協(xié)程退出函數(shù),則主協(xié)程可以正常的退出。
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 定時器
    +關(guān)注

    關(guān)注

    23

    文章

    3274

    瀏覽量

    116882
  • 數(shù)據(jù)結(jié)構(gòu)

    關(guān)注

    3

    文章

    573

    瀏覽量

    40540
  • 數(shù)組
    +關(guān)注

    關(guān)注

    1

    文章

    419

    瀏覽量

    26324
  • Redis
    +關(guān)注

    關(guān)注

    0

    文章

    382

    瀏覽量

    11266
收藏 人收藏

    評論

    相關(guān)推薦
    熱點推薦

    軟件定時器的特點和原理

    本文介紹了軟件定時器的特點和原理,并從時鐘節(jié)拍,數(shù)據(jù)結(jié)構(gòu)定時器操作等角度分析,實現(xiàn)了基于STM32的軟件定時器,該軟件
    發(fā)表于 08-19 08:29

    什么是軟件定時器?基于STM32的軟件定時器該怎樣去實現(xiàn)

    目錄1.什么是軟件定時器2.軟件定時器實現(xiàn)原理3.基于STM32的軟件定時器3.1 時鐘節(jié)拍3.2 數(shù)據(jù)結(jié)構(gòu)3.3
    發(fā)表于 12-22 07:47

    GPIB命令的數(shù)據(jù)結(jié)構(gòu)

    針對GPIB命令的結(jié)構(gòu),提出一種存儲GPIB命令的數(shù)據(jù)結(jié)構(gòu)。根據(jù)GPIB命令的層次關(guān)系的特點,選擇數(shù)據(jù)結(jié)構(gòu)中“樹”的概念來存儲GPIB命令結(jié)點;并考慮程序
    發(fā)表于 02-10 16:20 ?70次下載

    GPIB命令的數(shù)據(jù)結(jié)構(gòu)

    針對GPIB命令的結(jié)構(gòu),提出一種存儲GPIB命令的數(shù)據(jù)結(jié)構(gòu)。根據(jù)GPIB命令的層次關(guān)系的特點,選擇數(shù)據(jù)結(jié)構(gòu)中“樹”的概念來存儲GPIB命令結(jié)點;并考慮程序
    發(fā)表于 01-04 10:13 ?0次下載

    定時器結(jié)構(gòu)及工作模式

    定時器是單片機的重要功能模塊之一,在檢測、控制領(lǐng)域有廣泛應(yīng)用。 定時器常用作定時時鐘,以實現(xiàn)定時檢測、
    發(fā)表于 09-25 10:08 ?4次下載
    <b class='flag-5'>定時器</b>的<b class='flag-5'>結(jié)構(gòu)</b>及工作模式

    數(shù)據(jù)結(jié)構(gòu)是什么_數(shù)據(jù)結(jié)構(gòu)有什么用

    數(shù)據(jù)結(jié)構(gòu)是計算機存儲、組織數(shù)據(jù)的方式。數(shù)據(jù)結(jié)構(gòu)是指相互之間存在一種或多種特定關(guān)系的數(shù)據(jù)元素的集合。通常情況下,精心選擇
    發(fā)表于 11-17 14:45 ?1.6w次閱讀
    <b class='flag-5'>數(shù)據(jù)結(jié)構(gòu)</b>是什么_<b class='flag-5'>數(shù)據(jù)結(jié)構(gòu)</b>有什么用

    降低了CPU負載率的μC/OS-II定時器有效改進方法

    對μC/OS-II的定時器管理算法進行改進的主要目標(biāo)是:要么不對定時器進行檢查,要檢查則一定有定時器到期[2]。為了達到這個設(shè)計目標(biāo),需要對μC/OS-II的定時器輪進行重新設(shè)計。采用
    發(fā)表于 07-19 07:06 ?1079次閱讀
    降低了CPU負載率的μC/OS-II<b class='flag-5'>定時器</b>有效改進方法

    什么是數(shù)據(jù)結(jié)構(gòu)?為什么要學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)數(shù)據(jù)結(jié)構(gòu)的應(yīng)用實例分析

    本文檔的主要內(nèi)容詳細介紹的是什么是數(shù)據(jù)結(jié)構(gòu)?為什么要學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)數(shù)據(jù)結(jié)構(gòu)的應(yīng)用實例分析包括了:數(shù)據(jù)結(jié)構(gòu)在串口通信當(dāng)中的應(yīng)用,數(shù)據(jù)結(jié)構(gòu)在按鍵
    發(fā)表于 09-26 15:45 ?14次下載
    什么是<b class='flag-5'>數(shù)據(jù)結(jié)構(gòu)</b>?為什么要學(xué)習(xí)<b class='flag-5'>數(shù)據(jù)結(jié)構(gòu)</b>?<b class='flag-5'>數(shù)據(jù)結(jié)構(gòu)</b>的應(yīng)用實例分析

    STM32基于cubeMX實現(xiàn)定時器點燈

    Cortex M3內(nèi)核當(dāng)中的定時器,它并不屬于芯片廠商的外設(shè),也就是說使用ARM內(nèi)核的不同廠商,都擁有基本結(jié)構(gòu)相同的系統(tǒng)定時器。主要目的是給RTOS提供時鐘節(jié)拍做時間基準(zhǔn)。基本定時器
    發(fā)表于 11-23 18:21 ?19次下載
    STM32基于cubeMX<b class='flag-5'>實現(xiàn)</b><b class='flag-5'>定時器</b>點燈

    STM32定時器-基本定時器

    目錄定時器分類基本定時器功能框圖講解基本定時器功能時鐘源計數(shù)時鐘計數(shù)自動重裝載寄存
    發(fā)表于 11-23 18:21 ?32次下載
    STM32<b class='flag-5'>定時器</b>-基本<b class='flag-5'>定時器</b>

    定時器作用及實現(xiàn)定時器數(shù)據(jù)結(jié)構(gòu)選取介紹1

    定時器在各種場景都需要用到,比如游戲的Buff實現(xiàn),Redis中的過期任務(wù),Linux中的定時任務(wù)等等。顧名思義,定時器的主要用途是執(zhí)行定時
    的頭像 發(fā)表于 04-21 15:20 ?1419次閱讀
    <b class='flag-5'>定時器</b>作用及<b class='flag-5'>實現(xiàn)</b><b class='flag-5'>定時器</b><b class='flag-5'>數(shù)據(jù)結(jié)構(gòu)</b>選取介紹1

    定時器作用及實現(xiàn)定時器數(shù)據(jù)結(jié)構(gòu)選取介紹2

    定時器在各種場景都需要用到,比如游戲的Buff實現(xiàn),Redis中的過期任務(wù),Linux中的定時任務(wù)等等。顧名思義,定時器的主要用途是執(zhí)行定時
    的頭像 發(fā)表于 04-21 15:20 ?1344次閱讀
    <b class='flag-5'>定時器</b>作用及<b class='flag-5'>實現(xiàn)</b><b class='flag-5'>定時器</b><b class='flag-5'>數(shù)據(jù)結(jié)構(gòu)</b>選取介紹2

    STM32如何使用定時器實現(xiàn)微秒(us)級延時?

    如何使用定時器實現(xiàn)微秒級延時的步驟: 步驟 1:配置定時器 首先,需要選擇一個適合的定時器。大多數(shù)STM32微控制
    的頭像 發(fā)表于 11-06 11:05 ?7165次閱讀

    定時器設(shè)計實現(xiàn)

    返回ITimer類型的共享指針。其中ITimer類中定義了start和stop方法,用于啟動或停止當(dāng)前定時器。 TimerManager還有一個內(nèi)部類TimerMessageQueue用于實現(xiàn)
    的頭像 發(fā)表于 11-08 16:50 ?816次閱讀

    redis數(shù)據(jù)結(jié)構(gòu)的底層實現(xiàn)

    Redis是一種內(nèi)存鍵值數(shù)據(jù)庫,常用于緩存、消息隊列、實時數(shù)據(jù)分析等場景。它的高性能得益于其精心設(shè)計的數(shù)據(jù)結(jié)構(gòu)和底層實現(xiàn)。本文將詳細介紹Redis常用的
    的頭像 發(fā)表于 12-05 10:14 ?777次閱讀
    主站蜘蛛池模板: 噜噜噜色噜噜噜久久 | 天天操天天干天天爱 | 丁香五婷婷 | 午夜aaaaaaaaa视频在线 | 国产―笫一页―浮力影院xyz | 日韩a毛片免费全部播放完整 | 天天综合天天添夜夜添狠狠添 | 久久狠狠躁免费观看 | 男女视频在线观看免费高清观看 | 九九精品在线 | 日日干夜夜操s8 | 激情五月综合 | 久久久99精品免费观看精品 | 免费看国产一级特黄aa大片 | 国产一级又色又爽又黄大片 | 男人操女人在线观看 | 欧美在线精品一区二区三区 | 搡女人视频免费 | 伊人欧美在线 | 一区二区三区亚洲视频 | 日韩欧美一卡二区 | 热门国产xvideos中文 | 亚洲人成亚洲人成在线观看 | 六月综合网 | 色天使色护士 在线视频观看 | 手机看片福利盒子久久青 | 一级美女视频 | 最刺激黄a大片免费观看下截 | 五月激激| 俺也去第四色 | 亚洲高清中文字幕一区二区三区 | aa1在线天堂 | aa视频在线观看 | 亚洲综合在线一区 | 日本免费黄色小视频 | 国产精品你懂的在线播放 | 下农村女人一级毛片 | 二级黄色大片 | 九九热精品在线观看 | 丁香五月情| 美女视频网站色 |