我在高级Golang开发人员角色的一项编码访谈中被问到了这个问题。
这是一个机器编码,我不得不使用我选择的编程语言设计和实现高效的缓存,这应该非常有效,线程安全,并且不应太多的内存使用。
我选择了Golang,因为..
-
Golang对并发的内置支持,包括轻量级的goroutines和频道,使其成为建立具有大量并发操作的高性能系统的理想选择,例如高速缓存管理。
-
语言的简单且一致的语法,以及它的重点是最大程度地减少内存开销和最大化吞吐量,这也使其非常适合构建可扩展和高效的缓存系统。
-
此外,Golang的广泛标准库还包括用于处理基于时间的事件的软件包,例如本示例中使用的时间软件包,这简化了缓存到期策略的实现。
li> -
总的来说,Golang的并发支持,性能和简单性的组合使其成为建立高性能缓存系统的绝佳选择。
这是带有评论的代码,以下是
package main
import (
"fmt"
"sync"
"time"
)
type Cache struct {
data map[string]any
expiry time.Duration
evictPool chan struct{} // we don't want infinite goroutines
mutex sync.RWMutex
}
func NewCache(expiry time.Duration, evictPoolSize int) *Cache {
return &Cache{
data: make(map[string]any),
expiry: expiry,
evictPool: make(chan struct{}, evictPoolSize),
}
}
func (c *Cache) Set(key string, value any) {
c.mutex.Lock()
defer c.mutex.Unlock()
c.data[key] = value
go c.scheduleEviction(key) // schedule eviction
}
func (c *Cache) Get(key string) (any, bool) {
c.mutex.RLock()
defer c.mutex.RUnlock()
value, ok := c.data[key]
return value, ok
}
func (c *Cache) Delete(key string) {
c.mutex.Lock()
defer c.mutex.Unlock()
delete(c.data, key)
}
func (c *Cache) scheduleEviction(key string) {
select {
case c.evictPool <- struct{}{}:
// Goroutine acquired from pool, proceed with eviction
case <-time.After(c.expiry):
// Timed out waiting for goroutine from pool, evicting from current goroutine
c.evict(key)
return
}
// start eviction goroutine
go func() {
c.evict(key)
<-c.evictPool // free the pool, so more eviction can be scheduled
}()
}
func (c *Cache) evict(key string) {
c.mutex.Lock()
defer c.mutex.Unlock()
if _, ok := c.data[key]; ok {
delete(c.data, key)
fmt.Printf("Evicted key %s\n", key)
}
}
func main() {
cache := NewCache(2*time.Second, 10)
cache.Set("key1", "value1")
cache.Set("key2", "value2")
cache.Set("key3", "value3")
time.Sleep(3 * time.Second)
// Key1 should have been evicted, key2 and key3 should still be present
if _, ok := cache.Get("key1"); !ok {
fmt.Println("key1 evicted")
}
if _, ok := cache.Get("key2"); ok {
fmt.Println("key2 present")
}
if _, ok := cache.Get("key3"); ok {
fmt.Println("key3 present")
}
}
解释
我们还使用缓冲频道驱逐池来限制可驱逐键可产生的goroutines数量。
如果池中有goroutine,我们将其用于驱逐。否则,我们等待按时间指定的超时。从当前的goroutine驱逐后。
驱逐后,goroutine被释放回池。
使用线程/goroutine池是一个更好的选择,而无需检查就产生goroutines,因为这限制了使用中的内存,如果未检查,则可以无限。
一个重要的说明
这个程序仍然远非完美,其想法是向您展示您使用频道来创建一个goroutine池,但是在高吞吐量系统的情况下,我们将在下一个中为此设计一个更好的系统张贴。
请鼓掌!
如果您发现这篇文章很有帮助,我将感谢一些拍手ðððð7,它会激励我将来写更多这样有用的文章。
跟着我获取常规的令人敬畏的内容和见解。
订阅我的时事通讯
如果您喜欢我的内容,请考虑订阅我的免费新闻通讯,以获取直接交付给收件箱的独家,教育,技术,有趣和与职业有关的内容
重要链接
感谢您阅读帖子,请务必遵循以下链接以获取将来更多令人敬畏的内容。
Twitter:https://twitter.com/dsysd_dev
YouTube:https://www.youtube.com/@dsysd-dev
github:https://github.com/dsysd-dev
媒介:https://medium.com/@dsysd-dev
电子邮件:dsysd.mail@gmail.com
Linkedin: https://www.linkedin.com/in/dsysd-dev/
新闻通讯:https://dsysd.beehiiv.com/subscribe
Gumroad:https://dsysd.gumroad.com/
Dev.TO:https://dev.to/dsysd_dev/