GO的并发模式Context
GO的并发模式Context
文章目录
- GO的并发模式Context
- 一、介绍
- 二、Context
- 三、context的衍生
- 四、示例:Google Web Search
- 4.1 server程序
- 4.2 userip 包
- 4.3 google 包
- 五、使用context包中程序实体实现`sync.WaitGroup`同样的功能
- (1)使用`sync.WaitGroup`实现一对多goroutine协作流程多同步工具
- (2)使用context包中程序实体来实现
- 六、Context的特点
- (1)Context介绍:上下文、树、根节点
- (2)四个延伸Context值的函数
- (3)理解context包中“可撤销”和“撤销一个context”
- (4)撤销信号在上下文树中的传播
- (5)通过Context携带数据
一、介绍
参考:Go的并发模式Context。
在Go服务中,每一个传入进来的请求都将在它自己的goroutine中被处理。请求处理器通常启动额外的goroutines去访问后端(例如数据库和RPC服务)。基于请求进行工作的goroutines通常需要获取请求的特殊值,例如最终的用户身份、授权令牌、和请求的最后期限。当一个请求被取消或者超时,所有给予请求进行工作的goroutines应该立刻退出,以便于系统能够回收任何它们使用的资源。
在Google中,我们开发了一个context包,以便于更容易的传递请求范围的值、取消信号、和最终的读取API边界的期限,涉及处理请求中的所有goroutines。这个包是公开可用的,被称为context。这篇文章将详细描述如何使用这个包,并提供完成的工作示例。
二、Context
context包的核心代码是Context类型:
// 一个Context懈怠了终止日期、取消信号、和请求范围的值,读取API的边界。
// 它的方法对于多个goroutines同时使用是安全的。
type Context interface{// Done方法,返回一个通道。当Context被取消或者超时的时候,这个通道将被关闭。Done() <-chan struct{}// Err 用于表明为什么Context是被取消。通常是由于Done的通道被关闭Err() error// Deadline 当无论什么原因,这个context被取消的时候,返回一个时间,Deadline() (deadline time.Time, ok bool)// Value 返回与key对应的值,如果没有就返回nilValue(key interface{}) interface{}
}
Done方法返回一个通道,这个通道给那些代表Context运行函数的取消信号:当通道被关闭,当通道关闭,这些函数应该立刻放弃它们的工作并返回。Err函数返回一个错误,表明为什么Context 是被取消。在管道和消除这篇文件中更详细的探讨了Done 通道的完整用法。
一个Context没有一个Cancel方法,和Done通道仅用于接收的原因相同:函数通常是接收到一个取消信号,而不是发送一个信号。尤其是,当一个父操作为子操作启动goroutines,这些子操作不应该能够去取消父操作。相反,WithCancel函数(在下面描述)提供了一个方法用于取消一个新的Context值。
一个Context被多个goroutine同时使用时安全的。代码能够传统同一个Context给任何数量的goroutines,并取消Context以向所有goroutines发出信号。
Deadline方法允许函数去确定是否它们应该都开始工作;如果剩余的时间太少,它可能是没有价值的。代码也可以使用最后期限给I/O操作设置超时时间。
Value允许一个Context可以携带请求范围的值。这个数据对多个goroutines同时使用必须是安全的。
三、context的衍生
context包提供了一个函数用于从已经存在的Contexts里衍生新的Context值。这些值形成了树:当一个Context被取消,所有从它衍生出来的Context也将被取消。
Background是任何Context树的根,它永远不会被取消:
/// Background 返回一个空的context。它绝不会被取消,没有最后期限,不带值。
// Background 通常在main、init、test中使用,作为请求顶层的Context。
func Background() Context
WithCancel和WithTimeout返回衍生的Context值。它能比它父Context更早的取消。
关联一个传入请求的Context,当请求处理返回的时候,它通常能够被取消。
当使用多个副本的时候,WithCancel对于·取消冗余的请求也很有用。
WithTimeout对于设置后台服务请求的截止日期很有用。
// WithCancel 返回一个父Context的拷贝,它的Done通道在父级关闭时立即关闭。
// Done 完成关闭或取消
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
// 一个CancelFunc 取消一个Context
type CancelFunc func()// WithTimeout 返回一个父级的副本,它的Done 通道在父级关闭时立即关闭。
// 关闭 Done、调用取消或超时。
// 新Context的最后期限,是现在+超时时间 和 父的截止日期(如果有的话)中较早的一个。
// 如果计时器仍在运行,取消函数将释放其资源。
func WithTimeout(parent Context, timeout time.Duration) (context, CancelFunc)
WithContext 提供了一个方法,去将请求范围的值和Context进行关联。
// WithValue 返回一个父Context的副本, 它的Value方法返回key对应的val。
func WithValue(parent Context, key interface{}, value interface{}) Context
最好查看如何使用context包的方法是通过工作示例。
四、示例:Google Web Search
我们的示例是一个HTTP服务,它处理像/search?q=golang&timeout=1s这样的URL,通过转发"golang" 的查询,到Google Web Search API并展示结果。这个timeout参数告诉服务端在一段时间后取消请求。
代码被分到三个包中:
server提供main函数,并处理/search请求;userip提供用于从请求中提取用户ip地址的函数,并将它和Context进行关联;google提供查询函数用于发送请求到 Google;
4.1 server程序
server程序处理像/search?q=golang的请求,通过提供前几个对golang在Google上的查询结果。它注册handleSearch来处理/search端点。这个处理创建了一个初始的Context称之为ctx,当处理返回的时候,安排它取消。如果请求包含timeoutURL参数,当超时时间通过Context将自动被取消:
func main() {// 注册 handleSearch 来处理 /search 端点http.HandleFunc("/search", handleSearch)
}func handleSearch(w http.ResponseWriter, req *http.Request) {// ctx 是 这个处理器的 Context。// 调用 cancel 关闭ctx.Done 的通道。这是对这个请求的取消信号,被处理器启动。var (ctx context.Contextcancel context.CancelFunc)// 获取超时时间timeout, err := time.ParseDuration(req.FormValue("timeout"))if err == nil {// 请求有超时时间,因此创建一个context,当超时时间到期后它将自动取消ctx, cancel = context.WithTimeout(context.Background(), timeout)} else {ctx, cancel = context.WithCancel(context.Background())}// handleSearch返回之后,立刻取消ctxdefer cancel()
处理器从请求中提取查询,并通过调用userip包提取客户端的IP地址。客户端的IP地址对后端是需要的,因此handleSearch将它绑定到ctx上:
// 检查查询请求query := req.FormValue("q")if query == "" {http.Error(w, "no query", http.StatusBadRequest)return}userIP, err := userip.FromRequest(req)if err != nil {http.Error(w, err.Error(), http.StatusBadRequest)return}ctx = userip.NewContext(ctx, userIP)
handle调用google.Search方法,带有ctx和query:
// 运行Google 搜索,并打印结果start := time.Now()results, err := google.Search(ctx, query)elapsed := time.Since(start)
如果查询成功,处理器渲染结果
if err != nil {http.Error(w, err.Error(), http.StatusInternalServerError)return}if err := resultsTemplate.Execute(w, struct {Results google.ResultsTimeout, Elapsed time.Duration}{Results: results,Timeout: timeout,Elapsed: elapsed,}); err != nil {log.Print(err)return}
4.2 userip 包
userip包提供了用于从请求中提取用户ip地址的函数,并将它关联到Context中。一个Context提供了键-值对的映射,在这里键和值都是interface{}类型。键的类型必须支持判等操作,值在被多个goroutine同时使用多时候必须是安全的。像userip包,隐藏了这个映射的细节,并提供了抢类型用于读取一个特定的Context值。
为了避免键的冲突,userip定义了一个不对外开放的类型key,并使用这个类型的值作为context的键。
// 密钥类型未导出以防止与其他包中定义的上下文密钥发生冲突。
type key int// userIPKey 是一个context key,用于用户的IP地址。它的零值是随意定的。
// 如果这个包定义了其它的context key。它们应该是不同的数值。
const userIPKey key = 0
FromRequest从一个http.Request中提取一个userIP:
func FromRequest(req *http.Request) (net.IP, error) {ip, _, err := net.SplitHostPort(req.RemoteAddr)if err != nil {return nil, fmt.Errorf("userip: %q is not IP:port", req.RemoteAddr)}
NewContext返回一个新的Context,携带了一个提供的userIP值。
// 如果ip地址存在, FromContext 从ctx中提取用户的ip地址,
func FromContext(ctx context.Context) (net.IP, bool) {// 如果 ctx 没有针对key的值,ctx.Value 将返回 nil// net.IP 类型的断言 将对 nil 返回 ok=falseuserIP, ok := ctx.Value(userIPKey).(net.IP)return userIP, ok
}
4.3 google 包
google.Search函数创建一个HTTP请求给Google Web Search API,并解析JSON编码的结果。它接受一个Context参数,在请求被处理期间,如果ctx.Done被关闭,它将立即返回。
Google Web Search API请求,包含了查询请求,并使用IP作为请求参数。
// Search 发送查询到 Google 搜索,并返回结果
func Search(ctx context.Context, query string) (Results, error) {// 准备Google 搜索 API 请求req, err := http.NewRequest("GET", "https://ajax.googleapis.com/ajax/services/search/web?v=1.0", nil)if err != nil {return nil, err}q := req.URL.Query()q.Set("q", query)// 如果 ctx 懈怠了用户的IP地址,将它转发给服务器。// Google APIs 使用用户的IP地址来区分服务器发起的请求和最终用户的请求if userIP, ok := userip.FromContext(ctx); ok {q.Set("userip", userIP.String())}req.URL.RawQuery = q.Encode()
Search使用一个有用的函数,httpDo,用于发出http请求,并在请求或响应在处理期间,如果ctx.Done 被关闭,那么就取消它们。Search将一个闭包传递给httpDo,用于处理http响应。
// 发出HTTP请求,并处理响应。// 如果 ctx.Done 是被取消, httpDo 函数将取消请求。var results Resultserr = httpDo(ctx, req, func(resp *http.Response, err error) error {if err != nil {return err}defer resp.Body.Close()// 解析JSON格式的查询结果// https://developers.google.com/web-search/docs/#fonjevar data struct {ResponseData struct {Results []struct {TitleNoFormatting stringURL string}}}if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {return err}for _, res := range data.ResponseData.Results {results = append(results, Result{Title: res.TitleNoFormatting, URL: res.URL})}return nil})// httpDo 等待我们提供的闭包返回,所以在这里读取结果是安全的。return results, err
httpDo 函数返回http请求,并在一个新的goroutine中处理它的响应。在goroutine离开之前,如果ctx.Done被关闭,它将取消请求。
// httpDo 发起 HTTP 请求, 并使用响应调用 f 。
// 如果 ctx.Done 在请求或f函数运行期间被关闭,httpDo取消那个请求,等待f离开,并返回ctx.Err。
// 否则 返回 f 的 error
func httpDo(ctx context.Context, req *http.Request, f func(*http.Response, error) error) error {// 在一个goroutine中 运行HTTP请求, 并传递响应给 fc := make(chan error, 1)req = req.WithContext(ctx)go func() { c <- f(http.DefaultClient.Do(req)) }()select {case <-ctx.Done():<-c // 为了等待f 函数返回return ctx.Err()case err := <-c:return err}
}
五、使用context包中程序实体实现sync.WaitGroup同样的功能
(1)使用sync.WaitGroup实现一对多goroutine协作流程多同步工具
package mainimport ("fmt""sync""sync/atomic"
)func main() {coordinateWithWaitGroup()
}func coordinateWithWaitGroup() {total := 12stride := 3var num int32fmt.Printf("The number: %d [with sync.WaitGroup]\n", num)var wg sync.WaitGroupfor i := 1; i <= total; i += stride {wg.Add(stride)for j := 0; j < stride; j++ {go addNum(&num, i+j, wg.Done)}wg.Wait()}
}func addNum(numP *int32, id int, deferFunc func()) {defer func() {deferFunc()}()for i := 0; ; i++ {currNum := atomic.LoadInt32(numP)newNum := currNum + 1if atomic.CompareAndSwapInt32(numP, currNum, newNum) {fmt.Printf("The number: %d [%d-%d]\n", newNum, id, i)break} else {fmt.Printf("The CAS option failed. [%d-%d]\n", id, i)}}
}
(2)使用context包中程序实体来实现
func coordinateWithContext() {total := 12var num int32fmt.Printf("The number: %d [with context.Context]\n", num)cxt, cancelFunc := context.WithCancel(context.Background())for i := 1; i <= total; i++ {go addNum(&num, i, func() {// 如果所有的addNum函数都执行完毕,那么就立即分发子任务的goroutine// 这里分发子任务的goroutine,就是执行 coordinateWithContext 函数的goroutine.if atomic.LoadInt32(&num) == int32(total) {// <-cxt.Done() 针对该函数返回的通道进行接收操作。// cancelFunc() 函数被调用,针对该通道的接收会马上结束。// 所以,这样做就可以实现“等待所有的addNum函数都执行完毕”的功能cancelFunc()}})}<-cxt.Done()fmt.Println("end.")
}
$ go run demo01.go
The number: 0 [with context.Context]
The number: 1 [12-0]
The number: 3 [6-0]
The number: 4 [7-0]
The number: 5 [8-0]
The number: 6 [9-0]
The number: 2 [5-0]
The number: 8 [10-0]
The number: 10 [11-0]
The number: 11 [1-0]
The number: 9 [3-0]
The number: 7 [2-0]
end.
执行发现,有时候结果并不对。
六、Context的特点
(1)Context介绍:上下文、树、根节点
Context类型是一种非常通用的同步工具,它的值不但可以任意扩散,还可以被用来传递额外的信息和信号。
Context类型可以提供一类代表上下文的值。此类值是并发安全的,也就是说它可以被传播给多个goroutine。
Context类型的值可以繁衍,这意味着可以通过一个Context值产生任意个子值。这些子值可以携带其父值的属性和数据,也可以响应我们通过父值传递的信号。
所有的Context值共同构成了一颗代表上下文全貌的树形结构。这棵树的树根(或称上下文的根节点)是一个已经在context包中预定义好的Context值,它是全局唯一的。通过context.Background函数可以取到它。
上下文根节点仅仅是一个最基本的支点,它不提供任何额外的功能。也就是说它既不可以被撤销,也不能携带任何数据。
(2)四个延伸Context值的函数
在context包中,包含四个用于繁衍Context值的函数,即WithCancel 、WithDeadline、WithTimeout、WithValue。
-
四个函数的第一个参数的类型都是
context.Context,名称都为parent。顾名思义,这个位置上的参数对应的都是它们将会产生的Context 值的父值。
-
WithCancel用于产生一个可撤销的parent的子值;
通过调用该函数,可以得到一个衍生自上下文根节点的Context值,和一个用于发送撤销信号的函数。
cxt, cancelFunc := context.WithCancel(context.Background()) -
WithDeadline和WithTimeout函数都是用来产生一个会定时撤销的parent的子值。 -
WithValue函数可以用来产生一个会携带额外数据的parent的子值。
(3)理解context包中“可撤销”和“撤销一个context”
在Context接口中,有两个与撤销息息相关的方法:
-
Done方法会返回一个元素类型为struct{}的接收通道。(让使用方感知撤销信号)这个接收通道的用途并不是传递元素值,而是让调用方法感知“撤销”当前Context值的那个信号。
一旦当前的Contex值被撤销,这里的接收通道会立刻关闭。对于一个未包含任何元素值的通道来说,它的关闭会使任何针对它的接收操作立即结束。
-
Err方法。让使用方得到撤销的具体原因。Context的Err方法的结果是error类型,并且其值值可能等于
context.Canceled变量的值,或者context.DeadlineExceeded变量的值。context.Canceled表示手动撤销;context.DeadlineExceeded表示由于给定的过期时间已到,而导致的撤销。
对“撤销“和”可撤销“对理解:
- 如果把”撤销“当作名词理解:指的是用来表达“撤销”状态的信号;
- 如果把“撤销”当作动词理解:指的是对撤销信号的表达;
- “可撤销”:指的是具有传达这种撤销信号的能力;
理解context.WithCancel:
-
通过调用
context.WithCancel可以产生一个可撤销的Context值,还会获得一个用于触发撤销信号的函数。 -
通过调用这个用于触发撤销信号的函数, 撤销信号会被传达给这个Context值,并由它的Done方法的结果值(一个接收通道)表达出来;
-
撤销函数只负责触发信号,而对应的可撤销的Context值也只负责传达信号。它们都不会去管后面具体的“撤销”操作。
实际上,我们的代码在感知到撤销操作以后,可以任意的操作,Context对此并没有任何的约束。
(4)撤销信号在上下文树中的传播
在contex包中,包含四个用于繁衍Context值的函数,其中WithCancel、WithDeadline、WithTimeout都是被用于基于给定的Context值产生可撤销的子值的。
context.WithValue函数得到的Context值可不撤销,撤销信号在被传播时,若遇到它们则会直接跨过,并试图将信号直接传给它们的子值。
context 包的WithCancel函数在被调用后会产生两个结果值,第一个结果值就是那个可撤销的Context值,而第二个则是用于触发撤销信号的函数。
撤销函数被调用之后,对应的Context值会先关闭它内部的接收通道,也就是它的Done方法会返回的那个通道。然后,它会向它的所有子值(或者说子节点)传达撤销信号。这些子值会如法炮制,把撤销信号继续传播下去。最后这个Context值会断开它与其父值之间的关联。
通过调用context包中的WithDealline函数,或者WithTimeout函数生成的Context值也是可撤销的。它们不但可以被手动撤销,还会依据在生成时被给定的过期时间,自动地进行撤销。
这里的定时撤销,是借助内部的计时器来做。
当过期时间到达时,WithDeadline和WithTimeout这两个Context值当行为与Context值被手动撤销时的行为几乎是一致的。只不过WithDeadline和WithTimeout会在最后停止并释放掉其内部的计时器。
(5)通过Context携带数据
WithValue函数在产生新的Context值的时候需要三个参数:父值、键、值。
与字典对于键的约束类似,这里键的类型必须是可判等的。原因很简单,我们从中获取数据的时候,它需要根据给定的键来查找对应的值。只不过,这种Context值并不是用字典来存储键和值的,只是简单的存储在响应的字段中。
Context类型的Value方法就是用来获取数据的。在我们调用含数据的Context值的Value方法时,它会先判断给定的键,是否与当前值中存储的键相等。如果相等就把该值中的存储的值直接返回,否则就到父值中继续寻找。如果父值中仍然未存储相等的键,那么该方法就会沿着上下文根节点的方向一路查找下去。
注意,除了含数据的Context值以外,其它几种Context值都无法携带数据。因此,Context值的Value方法在沿路查找的时候,会直接跨过那几种值。
Context接口并没有提供改变数据的方法。因此,在通常情况下,我们只能通过在上下文树中添加含数据的Context值来存储新的数据,或者通过撤销此种值的父值丢弃掉相应的数据。如果你存储在这里的数据可以从外部改变,那么必须自行保证安全。
相关文章:
GO的并发模式Context
GO的并发模式Context 文章目录GO的并发模式Context一、介绍二、Context三、context的衍生四、示例:Google Web Search4.1 server程序4.2 userip 包4.3 google 包五、使用context包中程序实体实现sync.WaitGroup同样的功能(1)使用sync.WaitGro…...
《Redis实战篇》六、秒杀优化
6、秒杀优化 6.0 压力测试 目的:测试1000个用户抢购优惠券时秒杀功能的并发性能~ ①数据库中创建1000用户 这里推荐使用开源工具:https://www.sqlfather.com/ ,导入以下配置即可一键生成模拟数据 {"dbName":"hmdp",…...
《C++ Primer Plus》第16章:string类和标准模板库(11)
其他库 C 还提供了其他一些类库,它们比本章讨论前面的例子更为专用。例如,头文件 complex 为复数提供了类模板 complex,包含用于 float、long 和 long double 的具体化。这个类提供了标准的复数运算及能够处理复数的标准函数。C11 新增的头文…...
声明和定义
前言 很多编程语言的语法中都有关于声明和定义的概念,这种概念一般会应用于函数或变量的创建和使用中,但是为什么要这么做? 以C语言为例,一些书籍或教程会要求读者在程序文件开头写上函数和变量的声明,然后再在后面对…...
Python获取最小路径,查找元素在list中的坐标
# codingutf-8__author__ Jeff.xiedef t(li):pass获取最小路径def minPathSum(grid):if not grid:return 0m len(grid) #m列n len(grid[0]) #n行print(grid[0])print("m: ",m)print("n: ",n)#创建一个二维数组dp [[0]*n for _ in range(m)]print(dp) #这…...
数据采集协同架构,集成马扎克、西门子、海德汉、广数、凯恩帝、三菱、海德汉、兄弟、哈斯、宝元、新代、发那科、华中各类数控以及各类PLC数据采集软件
文章目录 前言一、采集协同架构是什么?可以做什么(数控、PLC配置采集)?二、使用步骤 1.打开软件,配置MQTT或者数据库(支持sqlserver、mysql等)存储转发消息规则2.配置数控系统所采集的参数、转…...
Allegro172版本如何用自带的功能实现快速在1MMBGA下方等距放置电容
Allegro172版本如何用自带的功能实现快速在1MMBGA下方等距放置电容 在做PCB设计的时候,在1MM中心间距的BGA背面放置电容,是非常常见的设计,如何快速把电容等距放在BGA下方,除了借助辅助工具外,在Allegro升级到了172版本的时候,可以借助本身自带的功能实现快速放置,以下图…...
一种简单的统计pytorch模型参数量的方法
nelememt()函数Tensor.nelement()->引自Tensor.numel()->引自torch.numel(input)三者的作用是相同的Returns the total number of elements in the inputtensor.返回当前tensor的元素数量利用上面的函数刚好可以统计模型的参数数量parameters()函数Module.parameters(rec…...
【PyTorch】教程:对抗学习实例生成
ADVERSARIAL EXAMPLE GENERATION 研究推动 ML 模型变得更快、更准、更高效。设计和模型的安全性和鲁棒性经常被忽视,尤其是面对那些想愚弄模型故意对抗时。 本教程将提供您对 ML 模型的安全漏洞的认识,并将深入了解对抗性机器学习这一热门话题。在图像…...
中国区使用Open AI账号试用Chat GPT指南
最近推出强大的ChatGPT功能,各大程序员使用后发出感叹:程序员要失业了 不过在国内并不支持OpenAI账号注册,多数会提示: OpenAI’s services are not available in your country. 经过一番搜索后,发现如下方案可以完…...
STM32开发(9)----CubeMX配置外部中断
CubeMX配置外部中断前言一、什么是中断1.STM32中断架构体系2.外部中断/事件控制器(EXTI)3.嵌套向量中断控制器(NIVC)二、实验过程1.CubeMX配置2.代码实现3.硬件连接4.实验结果总结前言 本章介绍使用STM32CubeMX对引脚的外部中断进…...
Nextjs了解内容
目录Next.jsnext.js的实现1,nextjs初始化2, 项目结构3, 数据注入getInitialPropsgetServerSidePropsgetStaticProps客户端注入3,CSS Modules4,layout组件5,文件式路由6,BFF层的文件式路由7&…...
从事功能测试1年,裸辞1个月,找不到工作的“我”怎么办?
做功能测试一年多了裸辞职一个月了,大部分公司都要求有自动化测试经验,可是哪来的自动化测试呢? 我要是简历上写了吧又有欺诈性,不写他们给的招聘又要自动化优先,将项目带向自动化不是一个容易的事情,很多…...
机器学习基本原理总结
本文大部分内容参考《深度学习》书籍,从中抽取重要的知识点,并对部分概念和原理加以自己的总结,适合当作原书的补充资料阅读,也可当作快速阅览机器学习原理基础知识的参考资料。 前言 深度学习是机器学习的一个特定分支。我们要想…...
JVET-AC0315:用于色度帧内预测的跨分量Merge模式
ECM采用了许多跨分量的预测(Cross-componentprediction,CCP)模式,包括跨分量包括跨分量线性模型(CCLM)、卷积跨分量模型(CCCM)和梯度线性模型(GLM)࿰…...
Session与Cookie的区别(二)
脸盲症的困扰 小明身为杂货店的店长兼唯一的店员,所有大小事都是他一个人在处理。传统杂货店跟便利商店最大的差别在哪里?在于人情味。 就像是你去菜市场买菜的时候会被说帅哥或美女,或者是去买早餐的时候老板会问你:「一样&#…...
疫情开发,软件测试行情趋势是怎么样的?
如果说,2022年对于全世界来说,都是一场极大的挑战的话;那么,2023年绝对是机遇多多的一年。众所周知,随着疫情在全球范围内逐步得到控制,无论是国际还是国内的环境,都会呈现逐步回升的趋势&#…...
Java中间件描述与使用,面试可以用
myCat 用于切分mysql数据库(为什么要切分:当数据量过大时,mysql查询效率变低) ActiveMQ 订阅,消息推送 swagger 前后端分离,后台接口调式 dubbo 阿里的面向服务RPC框架,为什么要面向服务&#x…...
[OpenMMLab]AI实战营第七节课
语义分割代码实战教学 HRNet 高分辨率神经网络 安装配置 # 选择分支 git branch -a git switch 3.x # 配置环境 conda create -n mmsegmentation python3.8 conda activate mmsegmentation pip install torch1.11.0cu113 torchvision0.12.0cu113 torchaudio0.11.0 --extra-i…...
面向对象的设计模式
"万丈高楼平地起,7种模式打地基",模式是一种规范,我们应该站在巨人的肩膀上越看越远,接下来,让我们去仔细了解了解面向对象的7种设计模式7种设计模式设计原则的核心思想:找出应用中可能需要变化之…...
eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...
地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论
路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中(图1): mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...
免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...
怎么让Comfyui导出的图像不包含工作流信息,
为了数据安全,让Comfyui导出的图像不包含工作流信息,导出的图像就不会拖到comfyui中加载出来工作流。 ComfyUI的目录下node.py 直接移除 pnginfo(推荐) 在 save_images 方法中,删除或注释掉所有与 metadata …...
实战三:开发网页端界面完成黑白视频转为彩色视频
一、需求描述 设计一个简单的视频上色应用,用户可以通过网页界面上传黑白视频,系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观,不需要了解技术细节。 效果图 二、实现思路 总体思路: 用户通过Gradio界面上…...
Linux 下 DMA 内存映射浅析
序 系统 I/O 设备驱动程序通常调用其特定子系统的接口为 DMA 分配内存,但最终会调到 DMA 子系统的dma_alloc_coherent()/dma_alloc_attrs() 等接口。 关于 dma_alloc_coherent 接口详细的代码讲解、调用流程,可以参考这篇文章,我觉得写的非常…...
