40分钟学 Go 语言高并发:Context包与并发控制

news2024/11/24 17:44:09

Context包与并发控制

学习目标

知识点掌握程度应用场景
context原理深入理解实现机制并发控制和请求链路追踪
超时控制掌握超时设置和处理API请求超时、任务限时控制
取消信号传播理解取消机制和传播链优雅退出、资源释放
context最佳实践掌握使用规范和技巧工程实践中的常见场景

1. Context原理

1.1 Context基本结构和实现

让我们先看一个完整的Context使用示例:

package main

import (
    "context"
    "fmt"
    "log"
    "time"
)

// 请求追踪信息
type RequestInfo struct {
    TraceID    string
    SessionID  string
    StartTime  time.Time
}

// 服务接口
type Service interface {
    HandleRequest(ctx context.Context, req string) (string, error)
}

// 业务服务实现
type BusinessService struct {
    name string
}

func NewBusinessService(name string) *BusinessService {
    return &BusinessService{name: name}
}

// 处理请求
func (s *BusinessService) HandleRequest(ctx context.Context, req string) (string, error) {
    // 获取请求追踪信息
    info, ok := ctx.Value("request-info").(*RequestInfo)
    if !ok {
        return "", fmt.Errorf("request info not found in context")
    }

    log.Printf("[%s] Processing request: %s, TraceID: %s, Session: %s\n",
        s.name, req, info.TraceID, info.SessionID)

    // 模拟处理过程
    select {
    case <-time.After(2 * time.Second):
        return fmt.Sprintf("Result for %s", req), nil
    case <-ctx.Done():
        return "", ctx.Err()
    }
}

// 请求中间件
func requestMiddleware(next Service) Service {
    return &middlewareService{next: next}
}

type middlewareService struct {
    next Service
}

func (m *middlewareService) HandleRequest(ctx context.Context, req string) (string, error) {
    // 开始时间
    startTime := time.Now()

    // 添加请求信息到context
    info := &RequestInfo{
        TraceID:   fmt.Sprintf("trace-%d", time.Now().UnixNano()),
        SessionID: fmt.Sprintf("session-%d", time.Now().Unix()),
        StartTime: startTime,
    }
    ctx = context.WithValue(ctx, "request-info", info)

    // 调用下一个处理器
    result, err := m.next.HandleRequest(ctx, req)

    // 记录处理时间
    duration := time.Since(startTime)
    log.Printf("Request completed in %v, TraceID: %s\n", duration, info.TraceID)

    return result, err
}

func main() {
    // 创建服务
    service := requestMiddleware(NewBusinessService("UserService"))

    // 创建基础context
    ctx := context.Background()

    // 添加超时控制
    ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
    defer cancel()

    // 处理请求
    result, err := service.HandleRequest(ctx, "get user profile")
    if err != nil {
        log.Printf("Request failed: %v\n", err)
        return
    }

    log.Printf("Request succeeded: %s\n", result)

    // 模拟超时场景
    ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second)
    defer cancel()

    result, err = service.HandleRequest(ctx, "get user settings")
    if err != nil {
        log.Printf("Request failed: %v\n", err)
        return
    }
}

2. 超时控制

让我们实现一个带有超时控制的HTTP服务:

package main

import (
    "context"
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "time"
)

// 响应结构
type Response struct {
    Data  interface{} `json:"data,omitempty"`
    Error string      `json:"error,omitempty"`
}

// 服务配置
type ServiceConfig struct {
    Timeout        time.Duration
    MaxConcurrent  int
    RetryAttempts  int
    RetryDelay     time.Duration
}

// HTTP客户端包装器
type HTTPClient struct {
    client  *http.Client
    config  ServiceConfig
    limiter chan struct{} // 并发限制器
}

// 创建新的HTTP客户端
func NewHTTPClient(config ServiceConfig) *HTTPClient {
    return &HTTPClient{
        client: &http.Client{
            Timeout: config.Timeout,
        },
        config: config,
        limiter: make(chan struct{}, config.MaxConcurrent),
    }
}

// 发送HTTP请求
func (c *HTTPClient) DoRequest(ctx context.Context, method, url string) (*Response, error) {
    var lastErr error
    
    for attempt := 0; attempt <= c.config.RetryAttempts; attempt++ {
        select {
        case <-ctx.Done():
            return nil, ctx.Err()
        case c.limiter <- struct{}{}: // 获取并发许可
        }
        
        // 确保释放并发许可
        defer func() {
            <-c.limiter
        }()
        
        // 创建请求
        req, err := http.NewRequestWithContext(ctx, method, url, nil)
        if err != nil {
            return nil, fmt.Errorf("create request failed: %w", err)
        }
        
        // 设置请求超时
        reqCtx, cancel := context.WithTimeout(ctx, c.config.Timeout)
        defer cancel()
        
        // 执行请求
        resp, err := c.client.Do(req.WithContext(reqCtx))
        if err != nil {
            lastErr = err
            log.Printf("Request failed (attempt %d): %v\n", attempt+1, err)
            
            // 如果不是最后一次尝试,等待后重试
            if attempt < c.config.RetryAttempts {
                select {
                case <-ctx.Done():
                    return nil, ctx.Err()
                case <-time.After(c.config.RetryDelay):
                    continue
                }
            }
            continue
        }
        defer resp.Body.Close()
        
        // 解析响应
        var result Response
        if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
            return nil, fmt.Errorf("decode response failed: %w", err)
        }
        
        return &result, nil
    }
    
    return nil, fmt.Errorf("all retry attempts failed, last error: %v", lastErr)
}

// 处理HTTP请求的处理器
func handleRequest(w http.ResponseWriter, r *http.Request) {
    // 创建context
    ctx := r.Context()
    
    // 模拟长时间处理
    select {
    case <-time.After(2 * time.Second):
        response := Response{
            Data: "Request processed successfully",
        }
        json.NewEncoder(w).Encode(response)
    case <-ctx.Done():
        response := Response{
            Error: "Request timeout",
        }
        w.WriteHeader(http.StatusGatewayTimeout)
        json.NewEncoder(w).Encode(response)
    }
}

func main() {
    // 配置HTTP客户端
    config := ServiceConfig{
        Timeout:       5 * time.Second,
        MaxConcurrent: 10,
        RetryAttempts: 3,
        RetryDelay:    time.Second,
    }
    
    client := NewHTTPClient(config)
    
    // 创建HTTP服务器
    http.HandleFunc("/api", handleRequest)
    
    // 启动服务器
    go func() {
        log.Println("Server starting on :8080")
        if err := http.ListenAndServe(":8080", nil); err != nil {
            log.Fatal(err)
        }
    }()
    
    // 等待服务器启动
    time.Sleep(time.Second)
    
    // 测试请求
    ctx := context.Background()
    
    // 测试正常请求
    resp, err := client.DoRequest(ctx, "GET", "http://localhost:8080/api")
    if err != nil {
        log.Printf("Request failed: %v\n", err)
    } else {
        log.Printf("Response: %+v\n", resp)
    }
    
    // 测试超时请求
    ctx, cancel := context.WithTimeout(ctx, time.Second)
    defer cancel()
    
    resp, err = client.DoRequest(ctx, "GET", "http://localhost:8080/api")
    if err != nil {
        log.Printf("Request failed (expected): %v\n", err)
    } else {
        log.Printf("Response: %+v\n", resp)
    }
    
    // 保持主程序运行
    select {}
}
package main

import (
    "context"
    "fmt"
    "math/rand"
    "sync"
    "time"
)

// 请求处理器
type RequestHandler struct {
    requests  chan Request
    responses chan Response
    done      chan struct{}
    wg        sync.WaitGroup
}

// 请求结构
type Request struct {
    ID      int
    Timeout time.Duration
    Data    string
}

// 响应结构
type Response struct {
    RequestID int
    Result    string
    Error     error
}

// 创建新的请求处理器
func NewRequestHandler() *RequestHandler {
    return &RequestHandler{
        requests:  make(chan Request, 100),
        responses: make(chan Response, 100),
        done:      make(chan struct{}),
    }
}

// 启动处理器
func (h *RequestHandler) Start(workers int) {
    for i := 0; i < workers; i++ {
        h.wg.Add(1)
        go h.worker(i)
    }
}

// 工作协程
func (h *RequestHandler) worker(id int) {
    defer h.wg.Done()
    
    for {
        select {
        case req, ok := <-h.requests:
            if !ok {
                fmt.Printf("Worker %d: request channel closed\n", id)
                return
            }
            
            // 创建context用于超时控制
            ctx, cancel := context.WithTimeout(context.Background(), req.Timeout)
            
            // 处理请求
            response := h.processRequest(ctx, req)
            
            // 发送响应
            select {
            case h.responses <- response:
                fmt.Printf("Worker %d: sent response for request %d\n", 
                    id, req.ID)
            case <-h.done:
                cancel()
                return
            }
            
            cancel() // 清理context
            
        case <-h.done:
            fmt.Printf("Worker %d: received stop signal\n", id)
            return
        }
    }
}

// 处理单个请求
func (h *RequestHandler) processRequest(ctx context.Context, req Request) Response {
    // 模拟处理时间
    processTime := time.Duration(rand.Intn(int(req.Timeout))) + req.Timeout/2
    
    select {
    case <-time.After(processTime):
        return Response{
            RequestID: req.ID,
            Result:   fmt.Sprintf("Processed: %s", req.Data),
        }
    case <-ctx.Done():
        return Response{
            RequestID: req.ID,
            Error:    ctx.Err(),
        }
    }
}

// 提交请求
func (h *RequestHandler) SubmitRequest(req Request) error {
    select {
    case h.requests <- req:
        return nil
    case <-h.done:
        return fmt.Errorf("handler is stopped")
    }
}

// 获取响应
func (h *RequestHandler) GetResponse() (Response, error) {
    select {
    case resp := <-h.responses:
        return resp, nil
    case <-h.done:
        return Response{}, fmt.Errorf("handler is stopped")
    }
}

// 停止处理器
func (h *RequestHandler) Stop() {
    close(h.done)
    h.wg.Wait()
    close(h.requests)
    close(h.responses)
}

func main() {
    // 创建请求处理器
    handler := NewRequestHandler()
    handler.Start(3)
    
    // 发送一些测试请求
    requests := []Request{
        {ID: 1, Timeout: time.Second, Data: "Fast request"},
        {ID: 2, Timeout: time.Second * 2, Data: "Normal request"},
        {ID: 3, Timeout: time.Millisecond * 500, Data: "Quick request"},
        {ID: 4, Timeout: time.Second * 3, Data: "Slow request"},
    }
    
    // 提交请求
    for _, req := range requests {
        if err := handler.SubmitRequest(req); err != nil {
            fmt.Printf("Failed to submit request %d: %v\n", req.ID, err)
            continue
        }
        fmt.Printf("Submitted request %d\n", req.ID)
    }
    
    // 收集响应
    var wg sync.WaitGroup
    wg.Add(1)
    
    go func() {
        defer wg.Done()
        for i := 0; i < len(requests); i++ {
            resp, err := handler.GetResponse()
            if err != nil {
                fmt.Printf("Failed to get response: %v\n", err)
                continue
            }
            
            if resp.Error != nil {
                fmt.Printf("Request %d failed: %v\n", resp.RequestID, resp.Error)
            } else {
                fmt.Printf("Request %d succeeded: %s\n", resp.RequestID, resp.Result)
            }
        }
    }()
    
    // 等待所有响应处理完成
    wg.Wait()
    
    // 停止处理器
    handler.Stop()
    fmt.Println("Main: processing completed")
}

3. 取消信号传播

让我们继续完成取消信号传播的示例代码:

package main

import (
    "context"
    "fmt"
    "log"
    "sync"
    "time"
)

// 任务定义
type Task struct {
    ID      string
    Process func(ctx context.Context) (interface{}, error)
}

// 工作池
type WorkerPool struct {
    workers    int
    tasks      chan Task
    results    chan interface{}
    errors     chan error
    done       chan struct{}
    wg         sync.WaitGroup
}

// 创建新的工作池
func NewWorkerPool(workers int) *WorkerPool {
    return &WorkerPool{
        workers: workers,
        tasks:   make(chan Task, workers*2),
        results: make(chan interface{}, workers*2),
        errors:  make(chan error, workers*2),
        done:    make(chan struct{}),
    }
}

// 启动工作池
func (p *WorkerPool) Start(ctx context.Context) {
    // 启动workers
    for i := 0; i < p.workers; i++ {
        p.wg.Add(1)
        go p.worker(ctx, i)
    }

    // 等待所有worker完成
    go func() {
        p.wg.Wait()
        close(p.done)
        close(p.results)
        close(p.errors)
    }()
}

// worker处理任务
func (p *WorkerPool) worker(ctx context.Context, id int) {
    defer p.wg.Done()
    
    log.Printf("Worker %d started\n", id)
    
    for {
        select {
        case <-ctx.Done():
            log.Printf("Worker %d stopped: %v\n", id, ctx.Err())
            return
        case task, ok := <-p.tasks:
            if !ok {
                log.Printf("Worker %d: task channel closed\n", id)
                return
            }

            log.Printf("Worker %d processing task %s\n", id, task.ID)
            
            // 创建任务专用的context
            taskCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
            
            // 执行任务
            result, err := task.Process(taskCtx)
            cancel() // 释放任务context资源
            
            if err != nil {
                select {
                case p.errors <- fmt.Errorf("task %s failed: %w", task.ID, err):
                case <-ctx.Done():
                    return
                }
            } else {
                select {
                case p.results <- result:
                case <-ctx.Done():
                    return
                }
            }
        }
    }
}

// 提交任务
func (p *WorkerPool) Submit(task Task) error {
    select {
    case p.tasks <- task:
        return nil
    case <-p.done:
        return fmt.Errorf("worker pool is closed")
    }
}

// 关闭工作池
func (p *WorkerPool) Close() {
    close(p.tasks)
}

// 获取结果通道
func (p *WorkerPool) Results() <-chan interface{} {
    return p.results
}

// 获取错误通道
func (p *WorkerPool) Errors() <-chan error {
    return p.errors
}

func main() {
    // 创建根context
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()

    // 创建工作池
    pool := NewWorkerPool(3)
    pool.Start(ctx)

    // 创建模拟任务
    tasks := []Task{
        {
            ID: "task-1",
            Process: func(ctx context.Context) (interface{}, error) {
                select {
                case <-time.After(2 * time.Second):
                    return "Task 1 completed", nil
                case <-ctx.Done():
                    return nil, ctx.Err()
                }
            },
        },
        {
            ID: "task-2",
            Process: func(ctx context.Context) (interface{}, error) {
                select {
                case <-time.After(3 * time.Second):
                    return "Task 2 completed", nil
                case <-ctx.Done():
                    return nil, ctx.Err()
                }
            },
        },
        {
            ID: "task-3",
            Process: func(ctx context.Context) (interface{}, error) {
                select {
                case <-time.After(1 * time.Second):
                    return nil, fmt.Errorf("task 3 failed")
                case <-ctx.Done():
                    return nil, ctx.Err()
                }
            },
        },
    }

    // 提交任务
    for _, task := range tasks {
        if err := pool.Submit(task); err != nil {
            log.Printf("Failed to submit task %s: %v\n", task.ID, err)
        }
    }

    // 等待3秒后取消所有任务
    go func() {
        time.Sleep(3 * time.Second)
        log.Println("Cancelling all tasks...")
        cancel()
    }()

    // 收集结果和错误
    completed := 0
    expected := len(tasks)

    for completed < expected {
        select {
        case result, ok := <-pool.Results():
            if !ok {
                continue
            }
            log.Printf("Got result: %v\n", result)
            completed++
        case err, ok := <-pool.Errors():
            if !ok {
                continue
            }
            log.Printf("Got error: %v\n", err)
            completed++
        case <-ctx.Done():
            log.Printf("Main: context cancelled: %v\n", ctx.Err())
            completed = expected // 强制退出循环
        }
    }

    // 关闭工作池
    pool.Close()
    
    // 等待工作池完全关闭
    <-pool.done
    log.Println("All workers stopped")
}

3.1 取消信号传播流程图

在这里插入图片描述

4. Context最佳实践

4.1 Context使用规范

  1. 函数调用链传递
// 推荐
func HandleRequest(ctx context.Context, req *Request) error

// 不推荐
func HandleRequest(timeout time.Duration, req *Request) error
  1. Context应作为第一个参数
// 推荐
func ProcessTask(ctx context.Context, task *Task) error

// 不推荐
func ProcessTask(task *Task, ctx context.Context) error
  1. 不要储存Context在结构体中
// 不推荐
type Service struct {
    ctx context.Context
}

// 推荐
type Service struct {
    // 其他字段
}

func (s *Service) DoWork(ctx context.Context) error

4.2 Context使用注意事项

  1. 不要将nil传递给context参数
// 推荐
ctx := context.Background()
ProcessTask(ctx, task)

// 不推荐
ProcessTask(nil, task)
  1. context.Value应该只用于请求作用域数据
// 推荐
ctx = context.WithValue(ctx, "request-id", requestID)

// 不推荐 - 配置信息应该通过其他方式传递
ctx = context.WithValue(ctx, "db-config", dbConfig)
  1. 正确处理取消信号
select {
case <-ctx.Done():
    return ctx.Err()
default:
    // 继续处理
}

4.3 实践建议

  1. 超时控制
  • 设置合理的超时时间
  • 在不同层级使用不同的超时时间
  • 确保资源正确释放
  1. 错误处理
  • 区分超时和取消错误
  • 传递有意义的错误信息
  • 实现优雅降级
  1. 性能优化
  • 避免创建过多的context
  • 合理使用context.Value
  • 及时取消不需要的操作
  1. 日志追踪
  • 记录关键操作的耗时
  • 追踪请求的完整链路
  • 记录取消原因

总结

关键点回顾

  1. Context原理
  • 继承关系
  • 值传递机制
  • 生命周期管理
  1. 超时控制
  • 设置超时时间
  • 处理超时信号
  • 资源清理
  1. 取消信号传播
  • 信号传递机制
  • 取消处理流程
  • 资源释放
  1. 最佳实践
  • 使用规范
  • 注意事项
  • 优化建议

实践建议

  1. 代码规范
  • 遵循命名约定
  • 合理组织代码结构
  • 添加必要的注释
  1. 错误处理
  • 使用有意义的错误信息
  • 实现错误恢复机制
  • 记录错误日志
  1. 性能优化
  • 减少不必要的context创建
  • 避免context.Value滥用
  • 及时释放资源

怎么样今天的内容还满意吗?再次感谢观众老爷的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2246791.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【SpringMVC - 1】基本介绍+快速入门+图文解析SpringMVC执行流程

目录 1.Spring MVC的基本介绍 2.大致分析SpringMVC工作流程 3.SpringMVC的快速入门 首先大家先自行配置一个Tomcat 文件的配置 配置 WEB-INF/web.xml 创建web/login.jsp 创建com.ygd.web.UserServlet控制类 创建src下的applicationContext.xml文件 重点的注意事项和说明…

neo4j图数据库community-5.50创建多个数据库————————————————

1.找到neo4J中的conf文件&#xff0c;我的路径是&#xff1a;D:\Program Files\neo4j-community-5.5.0-windows\neo4j-community-5.5.0\conf 这里找自己的安装路径&#xff0c; 2.用管理员模式打开conf文件&#xff0c;右键管理员&#xff0c;记事本或者not 3.选中的一行新建一…

如何最简单、通俗地理解Python的迭代器?

我们知道迭代器&#xff08;iterator&#xff09;可以用for循环去取数&#xff0c;这和列表取数有什么区别呢&#xff1f; 想理解Python迭起器的差异&#xff0c;有个很简单的例子 打个比方&#xff0c;你去玩街头投篮机&#xff0c;可以投5个球&#xff0c;这里有两种方式&a…

JavaEE 【知识改变命运】02 多线程(1)

文章目录 线程是什么&#xff1f;1.1概念1.1.1 线程是什么&#xff1f;1.1.2 为什么要有线程1.1.3 进程和线程的区别1.1.4 思考&#xff1a;执行一个任务&#xff0c;是不是创建的线程或者越多是不是越好&#xff1f;&#xff08;比如吃包子比赛&#xff09;1.1.5 ) Java 的线程…

Linux内核USB2.0驱动框架分析--USB包

一&#xff0c; 包的组成 每个包都由SOP&#xff08;包起始域&#xff09;、SYNC&#xff08;同步域&#xff09;、Packet Content&#xff08;包内容&#xff09;、EOP&#xff08;包结束域&#xff09;四部分组成&#xff0c;其中SOP、SYNC、EOP为所有包共有的域&#xff0c…

云轴科技ZStack亮相2024 IDC中国生态峰会,共塑AI时代IT生态新格局

11月21日&#xff0c;2024 IDC中国生态峰会在北京举办&#xff0c;吸引了超过300位生态伙伴齐聚一堂&#xff0c;聚焦行业内最前沿的热点话题。本届峰会以“创见先机&#xff0c;智领风云”为主题&#xff0c;深入探讨宏观经济趋势、技术革新以及如何融合AI与数据技术&#xff…

C0029.在Clion中解决Debug时,提示Process finished with exit code -1的错误

1.错误提示 Process finished with exit code -12.解决办法 如上在使用Debug进行代码调试时&#xff0c;直接出现如上报错&#xff0c;解决办法就是直接点击运行程序&#xff0c;即可查出报错编号&#xff0c;然后根据报错编号来查找问题&#xff1b; 然后在网上就可以根据该…

07-Making a Bar Chart with D3.js and SVG

课程链接 Curran的课程&#xff0c;通过 D3.js 的 scaleLinear, max, scaleBand, axisLeft, axisBottom&#xff0c;根据 .csv 文件生成一个横向柱状图。 【注】如果想造csv数据&#xff0c;可以使用通义千问&#xff0c;关于LinearScale与BandScale不懂的地方也可以在通义千…

读取各种来源格式单细胞数据集构建seurat分析对象,代做生信分析

参考资料和分析注意事项 全流程的分析指导视频 演示数据集网盘文件 分析参数文件路径格式的特别提示 大家给要分析用到的文件路径或目录路径的时候&#xff0c;以D:/omics_tools/demo_data/scrnaseq/GSE189125/GSE189125_5prime_scRNAseq_seqbatchA_counts.txt.gz 这个文件为…

SQL-多表操作

前文所介绍的sql操作都是基于单表进行的&#xff0c;接下来我们来学习多表操作。 多表设计 在实际的项目开发中&#xff0c;会根据业务需求和业务模块之间的关系进行数据库表结构设计&#xff0c;由于业务之间相互关联&#xff0c;所以各个表结构之间也存在着各种联系&#xf…

c++ STL线程安全使用

c STL不是线程安全的&#xff0c;因此在多线程中使用的时候&#xff0c;操作同一个容器&#xff0c;会崩溃&#xff0c;因此需要解决线程安全的问题&#xff1a; 使用实例类似于以下&#xff1a; #include <thread> #include <vector> #include "thread_safe…

Swift 实现判断链表是否存在环:快慢指针法

文章目录 前言摘要描述题解答案题解代码题解代码分析示例测试及结果时间复杂度空间复杂度总结关于我们 前言 本题由于没有合适答案为以往遗留问题&#xff0c;最近有时间将以往遗留问题一一完善。 LeetCode - #141 环形链表 不积跬步&#xff0c;无以至千里&#xff1b;不积小流…

SpringCloud实用-OpenFeign 调用三方接口

文章目录 前言正文一、项目环境二、项目结构2.1 包的含义2.2 代理的场景 三、完整代码示例3.1 定义FeignClient3.2 定义拦截器3.3 配置类3.4 okhttp配置3.5 响应体3.5.1 天行基础响应3.5.2 热点新闻响应 3.6 代理类3.6.1 代理工厂3.6.2 代理客户端3.6.3 FeignClient的建造器 四…

C++设计模式行为模式———中介者模式

文章目录 一、引言二、中介者模式三、总结 一、引言 中介者模式是一种行为设计模式&#xff0c; 能让你减少对象之间混乱无序的依赖关系。 该模式会限制对象之间的直接交互&#xff0c; 迫使它们通过一个中介者对象进行合作。 中介者模式可以减少对象之间混乱无序的依赖关系&…

HarmonyOS:使用ArkWeb构建页面

一、简介 页面加载是Web组件的基本功能。根据页面加载数据来源可以分为三种常用场景&#xff0c;包括加载网络页面、加载本地页面、加载HTML格式的富文本数据。 页面加载过程中&#xff0c;若涉及网络资源获取&#xff0c;需要配置ohos.permission.INTERNET网络访问权限。 二、…

矩阵的拼接

矩阵的拼接分为横向拼接和纵向拼接 注意&#xff1a;横向拼接要求两矩阵行数相同&#xff0c;纵向拼接要求两矩阵列数相同 h o r z c a t horzcat horzcat和 v e r t c a t vertcat vertcat函数 h o r z c a t ( a , b ) horzcat(a,b) horzcat(a,b)将 a a a和 b b b横向拼接&a…

SpringCloud框架学习(第五部分:SpringCloud Alibaba入门和 nacos)

目录 十二、SpringCloud Alibaba入门简介 1. 基本介绍 2.作用 3.版本选型 十三、 SpringCloud Alibaba Nacos服务注册和配置中心 1.简介 2.各种注册中心比较 3.下载安装 4.Nacos Discovery服务注册中心 &#xff08;1&#xff09; 基于 Nacos 的服务提供者 &#xf…

Ollama vs VLLM:大模型推理性能全面测评!

最近在用本地大模型跑实验&#xff0c;一开始选择了ollama,分别部署了Qwen2.5-14B和Qwen2.5-32B&#xff0c;发现最后跑出来的实验效果很差&#xff0c;一开始一直以为prompt的问题&#xff0c;尝试了不同的prompt&#xff0c;最后效果还是一直不好。随后尝试了vllm部署Qwen2.5…

.NET9 - 新功能体验(一)

被微软形容为“迄今为止最高效、最现代、最安全、最智能、性能最高的.NET版本”——.NET 9已经发布有一周了&#xff0c;今天想和大家一起体验一下新功能。 此次.NET 9在性能、安全性和功能等方面进行了大量改进&#xff0c;包含了数千项的修改&#xff0c;今天主要和大家一起体…

LeetCode 144.二叉树的前序遍历

题目&#xff1a;给你二叉树的根节点 root &#xff0c;返回它节点值的 前序 遍历。 思路&#xff1a;根 左 右 代码&#xff1a; /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNod…