- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章golang模拟实现带超时的信号量示例代码由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
前言 。
最近在写项目,需要用到信号量等待一些资源完成,但是最多等待N毫秒。在看本文的正文之前,我们先来看下C语言里的实现方法.
在C语言里,有如下的API来实现带超时的信号量等待:
1
2
3
4
5
|
SYNOPSIS
#include <pthread.h>
int
pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
|
然后在查看golang的document后,发现golang里并没有实现带超时的信号量,官方文档在这里.
原理 。
我的业务场景是这样的:我有一个缓存字典,当多个用户请求1个不存在的key时,只有1个请求会穿透到后端,而所有用户都要排队等这个请求完成,或者超时返回.
怎么实现呢?其实稍微想一想cond的原理,就能模拟一个带超时的cond出来.
在golang里,要同时实现”挂起等待”和”超时返回”,一般得用select case语法,一个case等待阻塞的资源,一个case等待一个timer,这一点是非常确定的.
原本阻塞的资源应该通过条件变量的机制来实现完成通知,既然这里决定用select case,那么自然想到用channel来代替这个完成通知.
接下来的问题就是,很多请求者并发来获取这个资源,但是资源还没有准备好,所以大家都要排队并挂起,等待资源完成,并且当资源完成后通知大家.
所以,这里很自然要为这个资源做一个队列,每个请求者创建一个chan,并将chan放到队列里,接着select case等待这个chan的通知。而另一端,资源完成后遍历队列,通知每个chan即可.
最后一个问题是,只有第一个请求者才能穿透请求到后端,而后续请求者不应该穿透重复的请求,这可以通过判断缓存里是否有这个key作为判定首次的条件,而标记位init来判断请求者是否应该排队.
我的场景 。
上面是思路,下面是我的业务场景实现.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
func (cache *Cache) Get(key string, keyType int) *string {
if
keyType == KEY_TYPE_DOMAIN {
key =
"#"
+ key
}
else
{
key =
"="
+ key
}
cache.mutex.Lock()
item, existed := cache.dict[key]
if
!existed {
item = &cacheItem{}
item.key = &key
item.waitQueue = list.New()
cache.dict[key] = item
}
cache.mutex.Unlock()
conf := config.GetConfig()
lastGet := getCurMs()
item.mutex.Lock()
item.lastGet = lastGet
if
item.init {
// 已存在并且初始化
defer item.mutex.Unlock()
return
item.value
}
// 未初始化,排队等待结果
wait := waitItem{}
wait.wait_chan = make(chan *string, 1)
item.waitQueue.PushBack(&wait)
item.mutex.Unlock()
// 新增key, 启动goroutine获取初始值
if
!existed {
go cache.initCacheItem(item, keyType)
}
timer := time.NewTimer(time.Duration(conf.Cache_waitTime) * time.Millisecond)
var
retval *string = nil
// 等待初始化完成
select {
case
retval = <- wait.wait_chan:
case
<- timer.C:
}
return
retval
}
|
简述一下整个过程:
在initCacheItem函数里,数据已获取成功 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
// 一旦标记为init, 后续请求将不再操作waitQueue
item.mutex.Lock()
item.value = newValue
item.init =
true
item.expire = expire
item.mutex.Unlock()
// 唤醒所有排队者
waitQueue := item.waitQueue
for
elem := waitQueue.Front(); elem != nil; elem = waitQueue.Front() {
wait := elem.Value.(*waitItem)
wait.wait_chan <- newValue
waitQueue.Remove(elem)
}
|
最后 。
这样就实现了带超时的条件变量效果,实际上我的场景是一个broadcast的cond例子,大家可以参照思路实现自己想要的效果,活学活用.
总结 。
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对我的支持.
原文链接:https://yuerblog.cc/2017/09/04/golang-condition-variable-with-timeout/ 。
最后此篇关于golang模拟实现带超时的信号量示例代码的文章就讲到这里了,如果你想了解更多关于golang模拟实现带超时的信号量示例代码的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我正在尝试运行这段代码,用随机数替换字符串中的一个字符: //Get the position between 0 and the length of the string-1 to insert
我有一个包含 3 个位置的数组,假设它的所有位置都是数字 5。 [5 5 5] 我怎样才能以保持 555 的方式将它传递给 var?就像这样。 n:= 555 最佳答案 与使用任何其他语言的方式相同:
我使用 go dep 工具版本 v0.4.1,现在当我运行 dep init 时它会按预期创建 2 个文件,当我打开 gopkg.lock 我发现例如以下内容 [[projects]] name
我正在制作学习联系申请。我有一个 NewContact()。 // Contact - defines the fields of an entire Contact type Contact str
我一直在尝试使用该模块: https://godoc.org/github.com/hirochachacha/go-smb2#RemoteFile.ReadAt 为了在 Windows 机器上对我的
我需要在 golang 中编译 golang 中的程序。有没有不使用 exec.Command("go","build") 的原生形式? 最佳答案 不幸的是,我认为使用 exec.Command 是利
编写输出有效 go 代码的 go 应用程序可能最好使用内置的“go”包及其一些子包(“go/ast”、“go/token”、“go/printer”、等)。 要创建字符串文字表达式,您需要创建一个 a
我正在尝试使用 Golang 和 gin 为我的 api 和前端编写代理。如果请求转到除“/api”之外的任何内容,我想代理到 svelte 服务器。如果出现“/api/something”,我想在
我偶然发现了这个博客:using go as a scripting language并尝试创建一个可用于运行 golang 脚本的自定义图像,即 FROM golang:1.15 RUN go ge
我刚开始接触golang,我需要从json字符串中获取数据。 {"data" : ["2016-06-21","2016-06-22","2016-06-25"], "sid" : "ab", "di
关闭。这个问题是opinion-based .它目前不接受答案。 想要改进这个问题? 更新问题,以便 editing this post 可以用事实和引用来回答它. 关闭 3 年前。 Improve
我是 goland 的新手,试图在我的第一个项目中使用它。我注意到在 goland 中它没有显示通过容器引入的相同 golang SDK。 这是我的 Dockerfile: FROM golang:1
我正在试用 golang-neo4j-bolt-driver 包 github.com/johnnadratowski/golang-neo4j-bolt-driver 我已经导入了包并正在使用创建新
如果我安装了Go发行版软件包,则会在/usr/lib/golang/pkg中看到很多文件,在/usr/lib/golang/src中看到非常相似的文件集。这两组之间有什么关系? pkg是从src中的源
我发现 golang 上下文对于在客户端-服务器请求范围内取消服务器的处理很有用。 我可以使用 http.Request.WithContext 方法发出带有上下文的 http 请求,但是如果客户端不
我正在尝试将一个 golang 数组(还有 slice、struct 等)放置到 HTML 中,这样当从 golang gin web 框架返回 HTML 时,我可以在 HTML 元素内容中使用数组元
目前正在使用这个 ffmpeg 命令编辑视频 ffmpeg -i "video1.ts" -c:v libx264 -crf 20 -c:a aac -strict -2 "video1-fix.ts
我需要从 play.golang.org 链接读取 golang 代码并保存到 .go 文件。我想知道 play.golang.org 是否有任何公共(public) API 支持。我用谷歌搜索但没有
我第一次使用 IntelliJ 的最新 (2014-01-03) Golang 插件。 通常,我的终端工作流程是 go build && ./executable -args=1 所以我试图创建一个启
这个问题只是在构建之间随机出现,现在甚至我们的生产 repo,几个月都没有改变,在构建时也会出现这个问题。我已经坚持了一段时间。它不会发生在我们的本地机器上,只有在使用 dockerfile 时才会发
我是一名优秀的程序员,十分优秀!