これは興味深い質問です。私は、次のアイテムが破棄されるまで、破棄するアイテムのキューを維持するためにヒープを使用し、まさに時間の間スリープする解決策になります。私はそれがより効率的だと思うが、利得はスリムである場合もある。それにもかかわらず、あなたはここにコードを見ることができます:
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の経験に基づいて、約10^9のループを約1.2秒で処理することができます。つまり、10^6のスケールでは、リスト全体をトラバースするには約1ミリ秒以上の時間がかかります破壊コードとデータコピー(何千回もの実行で平均して100ミリ秒程度のコストがかかります)。私のコードのアプローチはO(lg N)で、10^6スケールで少なくとも1000倍高速です(定数を考慮して)。これらの計算はすべてベンチマークではなく経験に基づいていることに注意してください(ただし、私はそれらを提供することはできません)。
編集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です(理由は不思議です)。私のコードでは、ポインタ+ time.Timeは28バイトです。 100万の規模では、その差は小さい。 After Funcを使用する方がはるかに良い選択肢です。
あなたのユースケースについてもう少し説明できますか?より洗練されたソリューションがあるかもしれないので私は尋ねています。たとえば、MongoDBでは、タイムスタンプ上のTTLインデックスに基づいてドキュメントを自動削除できます。 –