当前位置: 首页 > news >正文

如何对上传的网站做代码修改婚纱店网页设计

如何对上传的网站做代码修改,婚纱店网页设计,能自己做游戏的软件,手机网站模板 商城文章目录 1 sync.Mutex2 支持并发读写3 主体结构 Group3.1 回调 Getter3.2 Group 的定义3.3 Group 的 Get 方法 4 测试 本文代码地址#xff1a; https://gitee.com/lymgoforIT/gee-cache/tree/master/day2-single-node 本文是7天用Go从零实现分布式缓存GeeCache的第二篇。 … 文章目录 1 sync.Mutex2 支持并发读写3 主体结构 Group3.1 回调 Getter3.2 Group 的定义3.3 Group 的 Get 方法 4 测试 本文代码地址 https://gitee.com/lymgoforIT/gee-cache/tree/master/day2-single-node 本文是7天用Go从零实现分布式缓存GeeCache的第二篇。 介绍 sync.Mutex 互斥锁的使用并实现 LRU 缓存的并发控制。实现 GeeCache 核心数据结构 Group缓存不存在时调用回调函数获取源数据代码约150行 1 sync.Mutex 多个协程(goroutine)同时读写同一个变量在并发度较高的情况下会发生冲突。确保一次只有一个协程(goroutine)可以访问该变量以避免冲突这称之为互斥互斥锁可以解决这个问题。 sync.Mutex 是一个互斥锁可以由不同的协程加锁和解锁。 sync.Mutex 是 Go 语言标准库提供的一个互斥锁当一个协程(goroutine)获得了这个锁的拥有权后其它请求锁的协程(goroutine) 就会阻塞在 Lock() 方法的调用上直到调用 Unlock() 锁被释放。 接下来举一个简单的例子假设有10个并发的协程打印了同一个数字100为了避免重复打印实现了printOnce(num int) 函数使用集合 set 记录已打印过的数字如果数字已打印过则不再打印。 var set make(map[int]bool, 0)func printOnce(num int) {if _, exist : set[num]; !exist {fmt.Println(num)}set[num] true }func main() {for i : 0; i 10; i {go printOnce(100)}time.Sleep(time.Second) }我们运行 go run . 会发生什么情况呢 $ go run . 100 100有时候打印 2 次有时候打印 4 次有时候还会触发 panic因为对同一个数据结构map的访问冲突了。接下来用互斥锁的Lock()和Unlock() 方法将冲突的部分包裹起来 var m sync.Mutex var set make(map[int]bool, 0)func printOnce(num int) {m.Lock()if _, exist : set[num]; !exist {fmt.Println(num)}set[num] truem.Unlock() }func main() {for i : 0; i 10; i {go printOnce(100)}time.Sleep(time.Second) }$ go run . 100相同的数字只会被打印一次。当一个协程调用了 Lock() 方法时其他协程被阻塞了直到Unlock()调用将锁释放。因此被包裹部分的代码就能够避免冲突实现互斥。 Unlock()释放锁还有另外一种写法 func printOnce(num int) {m.Lock()defer m.Unlock()if _, exist : set[num]; !exist {fmt.Println(num)}set[num] true }2 支持并发读写 上一篇文章 GeeCache 第一天 实现了 LRU 缓存淘汰策略。接下来我们使用 sync.Mutex 封装 LRU 的几个方法使之支持并发的读写。在这之前我们抽象了一个只读数据结构 ByteView 用来表示缓存值是 GeeCache 主要的数据结构之一。 day2-single-node/geecache/byteview.go package geecachetype ByteView struct {b []byte }// 实现Value接口从而可以作为lru缓存entry中的value值 func (v ByteView) Len() int {return len(v.b) }func (v ByteView) ByteSlice() []byte {return cloneBytes(v.b) }func (v ByteView) String() string {return string(v.b) }// 用于复制一个切片返回出去避免外部直接修改了缓存中的[]byte内容 func cloneBytes(b []byte) []byte {c : make([]byte, len(b))copy(c, b)return c } ByteView 只有一个数据成员b []byteb 将会存储真实的缓存值。选择 byte 类型是为了能够支持任意的数据类型的存储例如字符串、图片等。实现 Len() int 方法我们在 lru.Cache 的实现中要求被缓存对象必须实现 Value 接口即 Len() int 方法返回其所占的内存大小。b 是只读的使用 ByteSlice() 方法返回一个拷贝防止缓存值被外部程序修改。 接下来就可以为 lru.Cache 添加并发特性了。 day2-single-node/geecache/cache.go package geecacheimport (geecache/lrusync )type cache struct {mu sync.Mutexlru *lru.CachecacheBytes int64 }func (c *cache) add(key string, value ByteView) {c.mu.Lock()defer c.mu.Unlock()if c.lru nil {c.lru lru.New(c.cacheBytes, nil)}// 底层实际办事的还是委托给了lruc.lru.Add(key, value) }func (c *cache) get(key string) (value ByteView, ok bool) {c.mu.Lock()defer c.mu.Unlock()if c.lru nil {return}if v, ok : c.lru.Get(key); ok {return v.(ByteView), ok}return }cache.go 的实现非常简单实例化 lru封装 get 和 add 方法(底层实际是委托的lru在工作并添加互斥锁 mu。在 add 方法中判断了 c.lru 是否为 nil如果等于 nil 再创建实例。这种方法称之为延迟初始化(Lazy Initialization)一个对象的延迟初始化意味着该对象的创建将会延迟至第一次使用该对象时。主要用于提高性能并减少程序内存要求。 3 主体结构 Group Group 是 GeeCache 最核心的数据结构负责与用户的交互并且控制缓存值存储和获取的流程。 我们将在 geecache.go 中实现主体结构 Group那么 GeeCache 的代码结构的雏形已经形成了。 geecache/|--lru/|--lru.go // lru 缓存淘汰策略|--byteview.go // 缓存值的抽象与封装|--cache.go // 并发控制|--geecache.go // 负责与外部交互控制缓存存储和获取的主流程接下来我们将实现流程⑴和 ⑶远程交互的部分后续再实现。 3.1 回调 Getter 我们思考一下如果缓存不存在应从数据源文件数据库等获取数据并添加到缓存中。GeeCache 是否应该支持多种数据源的配置呢不应该一是数据源的种类太多没办法一一实现二是扩展性不好。如何从源头获取数据应该是用户决定的事情我们就把这件事交给用户好了。因此我们设计了一个回调函数(callback)在缓存不存在时调用这个函数得到源数据。 day2-single-node/geecache/geecache.go // A Getter loads data for a key. type Getter interface {Get(key string) ([]byte, error) }// A GetterFunc implements Getter with a function. // 接口型函数 type GetterFunc func(key string) ([]byte, error)// Get implements Getter interface function func (f GetterFunc) Get(key string) ([]byte, error) {return f(key) }定义接口 Getter 和 回调函数 Get(key string)([]byte, error)参数是 key返回值是 []byte。定义函数类型 GetterFunc并实现 Getter 接口的 Get 方法。函数类型实现某一个接口称之为接口型函数方便使用者在调用时既能够传入函数作为参数也能够传入实现了该接口的结构体作为参数。 了解接口型函数的使用场景可以参考 day2加餐 Go 接口型函数的使用场景 我们可以写一个测试用例来保证回调函数能够正常工作。 func TestGetter(t *testing.T) {var f Getter GetterFunc(func(key string) ([]byte, error) {return []byte(key), nil})expect : []byte(key)if v, _ : f.Get(key); !reflect.DeepEqual(v, expect) {t.Errorf(callback failed)} }在这个测试用例中我们借助 GetterFunc 的类型转换将一个匿名回调函数转换成了接口 f Getter。 调用该接口的方法 f.Get(key string)实际上就是在调用匿名回调函数。 定义一个函数类型 F并且实现接口 A 的方法然后在这个方法中调用自己。这是 Go 语言中将其他函数参数返回值定义与 F 一致转换为接口 A 的常用技巧。从而方便使用者在调用时既能够传入函数作为参数也能够传入实现了该接口的结构体作为参数 3.2 Group 的定义 接下来是最核心数据结构 Group 的定义 day2-single-node/geecache/geecache.go // A Group is a cache namespace and associated data loaded spread over type Group struct {name stringgetter GettermainCache cache }var (mu sync.RWMutex // 控制groups的并发安全groups make(map[string]*Group) )// NewGroup create a new instance of Group func NewGroup(name string, cacheBytes int64, getter Getter) *Group {if getter nil {panic(nil Getter)}mu.Lock()defer mu.Unlock()g : Group{name: name,getter: getter,mainCache: cache{cacheBytes: cacheBytes},}groups[name] greturn g }// GetGroup returns the named group previously created with NewGroup, or // nil if theres no such group. func GetGroup(name string) *Group {mu.RLock()g : groups[name]mu.RUnlock()return g }一个 Group 可以认为是一个缓存的命名空间每个 Group 拥有一个唯一的名称 name。比如可以创建三个 Group缓存学生的成绩命名为 scores缓存学生信息的命名为 info缓存学生课程的命名为 courses。第二个属性是 getter Getter即缓存未命中时获取源数据的回调(callback)。第三个属性是 mainCache cache即一开始实现的并发缓存。构建函数 NewGroup 用来实例化 Group并且将 group 存储在全局变量 groups 中。GetGroup 用来特定名称的 Group这里使用了只读锁 RLock()因为不涉及任何冲突变量的写操作。 3.3 Group 的 Get 方法 接下来是 GeeCache 最为核心的方法 Get // Get value for a key from cache func (g *Group) Get(key string) (ByteView, error) {if key {return ByteView{}, fmt.Errorf(key is required)}// Group主要是与用户交互的底层实际做事的是mainCache(cache而cache底层实际做事的是lru.Cacheif v, ok : g.mainCache.get(key); ok {log.Println([GeeCache] hit)return v, nil}return g.load(key) }func (g *Group) load(key string) (value ByteView, err error) {return g.getLocally(key) }func (g *Group) getLocally(key string) (ByteView, error) {bytes, err : g.getter.Get(key)if err ! nil {return ByteView{}, err}value : ByteView{b: cloneBytes(bytes)}g.populateCache(key, value)return value, nil }func (g *Group) populateCache(key string, value ByteView) {g.mainCache.add(key, value) }Get 方法实现了上述所说的流程 ⑴ 和 ⑶: 流程 ⑴ 从 mainCache 中查找缓存如果存在则返回缓存值。 流程 ⑶ 缓存不存在则调用 load 方法load 调用 getLocally分布式场景下会调用 getFromPeer 从其他节点获取getLocally 调用用户回调函数 g.getter.Get() 获取源数据并且将源数据添加到缓存 mainCache 中通过 populateCache 方法 至此这一章节的单机并发缓存就已经完成了。 4 测试 可以写测试用例也可以写 main 函数来测试这一章节实现的功能。那我们通过测试用例来看一下如何使用我们实现的单机并发缓存吧。 首先用一个 map 模拟耗时的数据库。 var db map[string]string{Tom: 630,Jack: 589,Sam: 567, }创建 group 实例并测试 Get 方法 func TestGet(t *testing.T) {loadCounts : make(map[string]int, len(db))gee : NewGroup(scores, 210, GetterFunc(func(key string) ([]byte, error) {log.Println([SlowDB] search key, key)if v, ok : db[key]; ok {if _, ok : loadCounts[key]; !ok {loadCounts[key] 0}loadCounts[key] 1return []byte(v), nil}return nil, fmt.Errorf(%s not exist, key)}))for k, v : range db {if view, err : gee.Get(k); err ! nil || view.String() ! v {t.Fatal(failed to get value of Tom)} // load from callback functionif _, err : gee.Get(k); err ! nil || loadCounts[k] 1 {t.Fatalf(cache %s miss, k)} // cache hit}if view, err : gee.Get(unknown); err nil {t.Fatalf(the value of unknow should be empty, but %s got, view)} }在这个测试用例中我们主要测试了 2 种情况 1在缓存为空的情况下能够通过回调函数获取到源数据。 2在缓存已经存在的情况下是否直接从缓存中获取为了实现这一点使用 loadCounts 统计某个键调用回调函数的次数如果次数大于1则表示调用了多次回调函数没有缓存。 测试结果如下 $ go test -run TestGetRUN TestGet 2024/07/20 14:33:58 [SlowDB] search key Tom 2024/07/20 14:33:58 [GeeCache] hit 2024/07/20 14:33:58 [SlowDB] search key Jack 2024/07/20 14:33:58 [GeeCache] hit 2024/07/20 14:33:58 [SlowDB] search key Sam 2024/07/20 14:33:58 [GeeCache] hit 2024/07/20 14:33:58 [SlowDB] search key unknown --- PASS: TestGet (0.00s) PASS可以很清晰地看到缓存为空时调用了回调函数第二次访问时则直接从缓存中读取。 原文地址https://geektutu.com/post/geecache-day2.html
http://www.dnsts.com.cn/news/239369.html

相关文章:

  • 78建筑网站静态网站设计心得
  • 深圳服饰网站建设网站做直播需要资质吗
  • 浙江建设技术职业学院网站资源网站模板
  • 云南做网站价格wordpress rss文件
  • c mvc 大型网站开发全球品牌网
  • 爱站网源码重庆网约车哪个平台最好
  • 公司网站建设支出计入崇左市住房和城乡建设局网站
  • 保险网站建设平台做阿里巴巴网站卖货咋样
  • 凡科做公司网站怎么收费淘宝天猫优惠券网站怎么做
  • 永嘉做网站网站系统安全性
  • 网站备案后怎么做军事新闻最新消息中国视频
  • 男生做污污的视频网站敬请期待前面一句
  • 新民个人网站建设优势网站建设技术教程
  • 工程信息网站谁做WordPress套
  • 注册企业网站wordpress点餐主题
  • 昆明网站建设高端定制网页设计实验报告分析与体会
  • 社区微网站建设方案ppt外贸营销型网站建站
  • 江苏招标网中标公告做搜狗网站优化
  • 中山品牌网站建设推广设计师关注的十大网站
  • 做网站卖多少钱一个wordpress宝塔伪静态
  • 网站建设任职要求上海网络维护服务
  • 做白酒网站网站用什么做内网穿透比较好
  • 网站建设求职具备什么怎么建公众号
  • 南京响应式网站建设网络维护协议范本
  • 番禺网站建设专家网络营销的推广方式都有哪些
  • 网站服务器空间大小php mysql开发的网站开发
  • 做网站为什么用php建设工程监理
  • 网站进度条源代码juqery-ui网站建设管理经验做法
  • 沈阳建设局网站市场推广计划怎么写
  • 网站页面设计要求网站备案号