이것은 흥미로운 질문입니다. 나는 다음 아이템이 파괴 될 때까지 파괴 될 아이템의 큐를 유지하기 위해 힙을 사용하고 정확한 시간 동안 sleep하는 솔루션을 찾는다. 나는 그것이 더 효율적이라고 생각하지만 이득은 어떤 경우에는 가늘다. 그럼에도 불구하고, 당신은 여기에 코드를 볼 수 있습니다
package main
import (
"container/heap"
"fmt"
"time"
)
type Item struct {
Expiration time.Time
Object interface{} // It would make more sence to be *interface{}, but not as convinient
}
//MINIT is the minimal interval for delete to run. In most cases, it is better to be set as 0
const MININT = 1 * time.Second
func deleteExpired(addCh chan Item) (quitCh chan bool) {
quitCh = make(chan bool)
go func() {
h := make(ExpHeap, 0)
var t *time.Timer
item := <-addCh
heap.Push(&h, &item)
t = time.NewTimer(time.Until(h[0].Expiration))
for {
//Check unfinished incoming first
for incoming := true; incoming; {
select {
case item := <-addCh:
heap.Push(&h, &item)
default:
incoming = false
}
}
if delta := time.Until(h[0].Expiration); delta >= MININT {
t.Reset(delta)
} else {
t.Reset(MININT)
}
select {
case <-quitCh:
return
//New Item incoming, break the timer
case item := <-addCh:
heap.Push(&h, &item)
if item.Expiration.After(h[0].Expiration) {
continue
}
if delta := time.Until(item.Expiration); delta >= MININT {
t.Reset(delta)
} else {
t.Reset(MININT)
}
//Wait until next item to be deleted
case <-t.C:
for !h[0].Expiration.After(time.Now()) {
item := heap.Pop(&h).(*Item)
destroy(item.Object)
}
if delta := time.Until(h[0].Expiration); delta >= MININT {
t.Reset(delta)
} else {
t.Reset(MININT)
}
}
}
}()
return quitCh
}
type ExpHeap []*Item
func (h ExpHeap) Len() int {
return len(h)
}
func (h ExpHeap) Swap(i, j int) {
h[i], h[j] = h[j], h[i]
}
func (h ExpHeap) Less(i, j int) bool {
return h[i].Expiration.Before(h[j].Expiration)
}
func (h *ExpHeap) Push(x interface{}) {
item := x.(*Item)
*h = append(*h, item)
}
func (h *ExpHeap) Pop() interface{} {
old, n := *h, len(*h)
item := old[n-1]
*h = old[:n-1]
return item
}
//Auctural destroy code.
func destroy(x interface{}) {
fmt.Printf("%v @ %v\n", x, time.Now())
}
func main() {
addCh := make(chan Item)
quitCh := deleteExpired(addCh)
for i := 30; i > 0; i-- {
t := time.Now().Add(time.Duration(i) * time.Second/2)
addCh <- Item{t, t}
}
time.Sleep(7 * time.Second)
quitCh <- true
}
놀이터 : 그런데 https://play.golang.org/p/JNV_6VJ_yfK
,이 작업 관리에 대한 cron
같은 패키지가 있지만 난 그렇게 내가 그들의 효율성을 말할 수없는 그들에 익숙하지 않다 .
편집 : 아직도 나는 의견을 충분한 명성을 :(성능에 대한 : 그것은 단지 자기를 해제로이 코드는 기본적으로 적은 CPU 사용량이있는 경우 전체 대신 파괴로 가입 될 필요 만 트래버스 항목 개인적으로 (실제로 ACM 경험을 기준으로) 대략적인 CPU는 약 1.2 초 안에 10^9의 루프를 처리 할 수 있습니다. 이는 10^6의 크기를 의미하며 전체 목록을 탐색하는 데는 평균값을 제외하고 약 1 밀리 초가 걸립니다 파괴 코드 및 데이터 복사 (100 밀리 초 정도의 규모로 수천 회 이상의 실행에 평균적으로 많은 비용이 소요됩니다).내 코드의 접근 방식은 O (lg N)이며, 10^6 배율은 적어도 1,000 배 더 빠릅니다 (상수를 고려함). 이 모든 계산은 벤치 마크가 아닌 경험에 기반을두고 있음을 다시 한 번 기억하십시오 (제공 할 수는 있지만 제공 할 수는 없습니다).
편집 2 : 두 번째 생각으로 , 나는 일반 솔루션은 간단한 최적화를 사용할 수 있습니다 생각이 변화
func deleteExpired(items []Item){
tail = len(items)
for index, v := range items { //better naming
if v.Expired(){
tail--
items[tail],items[index] = v,items[tail]
}
}
deleteditems := items[tail:]
items:=items[:tail]
}
, 그것은 더 이상 unefficiently 데이터를 복사하지 않고 여분의 공간을 할당하지 않습니다.
편집 3 : 변경 코드 here afterfunc의 메모리 사용을 테스트했습니다. 내 노트북에서는 호출 당 250 바이트이고, palyground에서는 69입니다 (이유는 궁금합니다). 내 코드, 포인터 + 시간. 시간은 28 바이트입니다. 백만의 규모에서, 그 차이는 희박합니다. After Func를 사용하는 것이 훨씬 더 좋은 방법입니다.
유스 케이스를 조금 더 설명 할 수 있습니까? 좀 더 우아한 해결책이 있기 때문에 부탁드립니다. 예를 들어 MongoDB는 타임 스탬프의 TTL 인덱스를 기반으로 문서를 자동으로 삭제합니다. –