Go 利用上下文进行并发计算

关注公众号【爱发白日梦的后端】分享技术干货、读书笔记、开源项目、实战经验、高效开发工具等,您的关注将是我的更新动力!

在Go编程中,上下文(context)是一个非常重要的概念,它包含了与请求相关的信息,如截止日期和取消信息,以及在请求处理管道中传递的其他数据。在并发编程中,特别是在处理请求时,正确处理上下文可以确保我们尊重和执行请求中设定的限制,如截止时间。

让我们通过一些代码示例来探讨如何在并发计算中使用上下文,以及如何在处理请求时尊重上下文所设定的截止日期和取消要求。

// download 函数用于下载给定 URL 的内容。
func download(ctx context.Context, url string) (string, error) {...}

download 函数尝试获取给定 URL 的内容。然而,需要注意的是,每个 URL 的下载内容可能不同,因此下载所需的时间也可能不同。如果在截止日期之前未能完成 URL 的下载,该函数将返回一个错误(截止日期错误)。

现在,假设我们需要下载许多 URL,并且我们只有有限的时间来完成这些下载。我们可以使用 errgroup 来并发地进行下载,如果超过截止时间,我们将取消所有并发操作。

// downloadAll 函数并发地下载给定 URL 的内容。
func downloadAll(ctx context.Context, urls []string) ([]string, error) {
  results := make([]string, len(urls))

  g, ctx := errgroup.WithContext(ctx)
  for i := range len(urls) {
    g.Go(func() error {
      content, err := download(ctx, urls[i])
      if err != nil {
        return err
      }

      results[i] = content
      return nil
    })
  }

  if err := g.Wait(); err != nil {
    return nil, err
  }

  return results, nil
}

在这个示例中,downloadAll 函数同时下载每个给定的 URL,并将相同的上下文传递给 download 函数。如果下载任何一个 URL 所需的时间超过了设定的截止时间,download 函数将失败,从而导致整个并发流程也失败,downloadAll 将返回一个截止日期错误。

除了下载这些 URL,我们还需要处理下载的内容。例如,我们可能要对每个 URL 的内容应用某个过滤器(谓词)。

// filter 函数检查给定内容是否符合给定的谓词。
func filter(content string, pred func(string) bool) bool {
  return pred(content)
}

请注意,过滤器既不需要上下文,也不进行任何跨边界调用。过滤器函数不关心上游处理的截止日期。

使用 filter 函数,我们可以定义一个过滤所有内容的函数。

// filterAll 函数同时过滤所有给定的内容。
func filterAll(contents []string, pred func(string) bool) []string {
  type Result struct {
    content string
    ok      bool
  }

  results := make([]Result, len(contents))

  g := errgroup.Group{}
  for i, content := range contents {
    g.Go(func() error {
      ok := filter(contents[i], pred)
      results[i] = Result{content: content, ok: ok}

      return nil
    })
  }

  g.Wait()

  var filtered []string
  for _, r := range results {
    if r.ok {
      filtered = append(filtered, r.content)
    }
  }

  return filtered
}

filterAll 函数调用 filter 函数来应用谓词到每个内容上,但谓词的应用可能会花费一些时间,可能超过上下文设置的截止时间。由于 filter 函数不使用上下文,因此它不会因为截止日期错误而失败。

我们需要重新定义 filterAll,使其使用上下文并检查其中的错误,而不管 filter 函数是否使用了上下文。

// filterAll 函数同时过滤所有内容,并检查上下文中的错误。
func filterAll(ctx context.Context, contents []string, pred func(string) bool) ([]string, error) {
  type Result struct {
    content string
    ok      bool
  }

  results := make([]Result, len(contents))

  g, ctx := errgroup.WithContext(ctx)
  for i, content := range contents {
    g.Go(func() error {
      if err := ctx.Err(); err != nil {
        return err
      }

      ok := filter(contents[i], pred)
      results[i] = Result{content: content, ok: ok}

      return nil
    })
  }

  if err := g.Wait(); err != nil {
    return nil, err
  }

  var filtered []string
  for _, r := range results {
    if r.ok {
      filtered = append(filtered, r.content)
    }
  }

  return filtered, nil
}

我们的新实现 filterAll 函数会检查上下文中的任何错误,即使上下文并未直接传递给下游函数(在本例中为 filter)。如果发生了与上下文相关的截止日期(或任何其他错误),整个过滤过程就会失败。

现在,让我们完成对所有内容的处理。

// processURLs 函数下载每个 URL 的内容并对其进行过滤。
//
// 处理必须在上下文截止日期内完成。
func processURLs(ctx context.Context, urls []string) ([]string, error) {
  contents, err := downloadAll(ctx, urls)
  if err != nil {
    return nil, err
  }

  filtered, err := filterAll(ctx, contents, somePredicate)

  return filtered, err
}

如果任何一个下载操作花费的时间过长,那么在尝试获取内容时就会发生截止日期错误,因为上下文被直接用于 API 调用。因此,downloadAll 函数也会失败,进而导致 processURLs 失败。

如果所有的 URL 在截止日期内都被正确下载,我们将继

续对它们进行过滤。在对每个下载内容进行过滤时,不使用上下文,但 filterAll 函数明确地检查上下文中的错误,如果发生了与上下文相关的截止日期(或任何其他错误),整个过滤过程就会失败。

有时候,仅仅使用 errgroup.WithContext 是不足以检测到上下文中的截止日期或其他问题的,特别是当上下文未直接使用时。因此,我们应该定期检查是否仍在时间限制内,否则就会失败。

最后,我们可以通过编写 filterAll 的测试来确保我们正确地处理了类似的情况,以确保我们尊重与上下文相关的任何错误。

func TestContextError(t *testing.T) {
  ctx, done := context.WithTimeout(context.Background(), time.Nanosecond)
  defer done()

  // 生成我们想要应用过滤器的一些数据。
  var contents []string = testingContent()

  _, err := filterAll(ctx, contents, thePredicate)
  if err == nil {
    t.Errorf("filterAll() = %v, want error", err)
  }
}

请注意,在测试中,我们期望 filterAll 会失败,因为我们设置的超时时间只有一纳秒。因此,上下文应该因为超过截止时间而发生错误。如果在启动 Goroutine 进行下载内容过滤时不检查 context.Err(),我们将永远不会处理此类错误。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Bluejay电调固件修改自检音乐、自定义启动音乐旋律

Bluejay电调固件修改自检音乐、自定义启动音乐旋律 Bluejay电调固件基本介绍Bluejay电调固件特点修改自检音乐、启动音乐旋律准备材料修改过程 Bluejay固件旋律音乐格式开头部分音符部分 收集到的音乐代码 Bluejay电调固件基本介绍 Bluejay是一种数字电调固件,用于控…

Stable Diffusion 3 发布及其重大改进

1. 引言 就在 OpenAI 发布可以生成令人瞠目的视频的 Sora 和谷歌披露支持多达 150 万个Token上下文的 Gemini 1.5 的几天后,Stability AI 最近展示了 Stable Diffusion 3 的预览版。 闲话少说,我们快来看看吧! 2. 什么是Stable Diffusion…

运维SRE-08 网络基础与进阶

今日内容 - **定时备份案例进阶.** - **定时巡检(检查系统基础指标),写入到文件中.** - 网络(抽象) 掌握与吸收时间: 直到课程结束.(第2阶段结束) - 网络基础: 网络概述,网络结构,网络设备. - 网络核心: OSI7层模型 ※※※※※※TCP/IP 3次握手 ※※※※※※TCP/IP 4…

Django入门指南:从环境搭建到模型管理系统的完整教程

环境安装: ​ 由于我的C的Anaconda 是安装在C盘的,但是没内存了,所有我将环境转在e盘,下面的命令是创建环境到指定目录中. conda create --prefixE:\envs\dj42 python3.9进入环境中: conda activate E:\envs\dj42…

【并发】CAS原子操作

1. 定义 CAS是Compare And Swap的缩写,直译就是比较并交换。CAS是现代CPU广泛支持的一种对内存中的共享数据进行操作的一种特殊指令,这个指令会对内存中的共享数据做原子的读写操作。其作用是让CPU比较内存中某个值是否和预期的值相同,如果相…

C#与VisionPro联合开发——串口通信

串口通信 串口通信是一种常见的数据传输方式,通过串行接口(串口)将数据以串行比特流的形式进行传输。在计算机和外部设备之间,串口通信通常是通过串行通信标准(如RS-232)来实现的。串口通信可以用于连接各…

AtCoder ABC342 A-D题解

华为出的比赛&#xff1f; 好像是全站首个题解哎&#xff01; 比赛链接:ABC342 Problem A: 稍微有点含金量的签到题。 #include <bits/stdc.h> using namespace std; int main(){string S;cin>>S;for(int i0;i<s.size();i){if(count(S.begin(),S.end(),S[i…

《穿越火线:枪战王者》手游客户端技术方案: 实时同步与手感优化 转载

一、项目背景 CF手游的团队有着相当丰富的FPS游戏制作经验&#xff0c;但是移动端开发经验相对匮乏。团队面对的挑战很大&#xff0c;我们需要在手机端完美还原CF十多个游戏模式&#xff0c;上百把枪械手感。 虽然我们有实时对战FPS游戏开发经验&#xff0c;但是手游网络质量…

H5获取手机相机或相册图片两种方式-Android通过webview传递多张照片给H5

需求目的&#xff1a; 手机机通过webView展示H5网页&#xff0c;在特殊场景下&#xff0c;需要使用相机拍照或者从相册获取照片&#xff0c;上传后台。 完整流程效果&#xff1a; 如下图 一、H5界面样例代码 使用html文件格式&#xff0c;文件直接打开就可以展示布局&#…

从源码学习单例模式

单例模式 单例模式是一种设计模式&#xff0c;常用于确保一个类只有一个实例&#xff0c;并提供一个全局访问点。这意味着无论在程序的哪个地方&#xff0c;只能创建一个该类的实例&#xff0c;而不会出现多个相同实例的情况。 在单例模式中&#xff0c;常用的实现方式包括懒汉…

【C语言】linux内核ipoib模块 - ipoib_send

一、中文注释 int ipoib_send(struct net_device *dev, struct sk_buff *skb,struct ib_ah *address, u32 dqpn) {struct ipoib_dev_priv *priv ipoib_priv(dev); // 获取IPoIB设备的私有数据struct ipoib_tx_buf *tx_req; // 发送请求结构体int hlen, rc; // 分别为头部长度…

安装 WSL 报错 Error code: Wsl/WININET_E_NAME_NOT_RESOLVED 问题解决

问题描述 在执行 wsl --install 安装Windows子系统Linux WSL (Windows Subsystem for Linux) 时报错&#xff1a; 无法从“https://raw.githubusercontent.com/microsoft/WSL/master/distributions/DistributionInfo.json”中提取列表分发。无法解析服务器的名称或地址 Error…

如何在本地电脑部署HadSky论坛并发布至公网可远程访问【内网穿透】

文章目录 前言1. 网站搭建1.1 网页下载和安装1.2 网页测试1.3 cpolar的安装和注册 2. 本地网页发布2.1 Cpolar临时数据隧道2.2 Cpolar稳定隧道&#xff08;云端设置&#xff09;2.3 Cpolar稳定隧道&#xff08;本地设置&#xff09;2.4 公网访问测试 总结 前言 经过多年的基础…

2000-2022年上市公司全要素生产率测算数据合计(原始数据+计算代码+结果)(LP法+OLS法+GMM法+固定效应法)

2000-2022年上市公司全要素生产率测算数据合计&#xff08;原始数据计算代码结果&#xff09;&#xff08;LP法OLS法GMM法固定效应法&#xff09; 1、时间&#xff1a;2000-2022年 2、范围&#xff1a;上市公司 3、指标&#xff1a;证券代码、证券简称、统计截止日期、固定资…

怎么自学python,大概要多久?python多久上手?

无限时长~~~~技术不断在更新&#xff0c;你的自学不也需要一直进行吗&#xff1f; 但如果是问&#xff1a;自学多长时间可以入门&#xff1f;或者可以找到工作&#xff1f;那我可以告诉你答案。 从零基础开始自学Python&#xff0c;依照每个人理解能力的不同&#xff0c;大致…

免费的WP模板网站推荐

免费wordpress模板下载 高端大气上档次的免费wordpress主题&#xff0c;首页大图全屏显示经典风格的wordpress主题。 https://www.wpniu.com/themes/289.html wordpress免费企业主题 深蓝色经典实用的wordpress网站模板&#xff0c;用wordpress免费企业主题搭建网站。 http…

Linux进程【补充】

文章目录 进程概念task_struct 进程创建forkvfork写时拷贝 进程状态僵尸进程孤儿进程守护进程 进程地址空间是什么为什么怎么做 进程概念 进程是一个程序的执行实例或者是担当系统资源分配的实体。当一个程序运行时&#xff0c;被从硬盘加载到内存中&#xff0c;操作系统为每个…

python 线程笔记一 (概念+示例代码)

1. 线程的概念 线程&#xff0c;可简单理解为是程序执行的一条分支&#xff0c;也是程序执行流的最小单元。线程是被系统独立调度和分派的基本单位&#xff0c;线程自己不拥有系统资源&#xff0c;只拥有一点儿在运行中必不可少的资源&#xff0c;但它可与同属于一个进程的其它…

第10届蓝桥杯Scratch图形化编程 选拔赛初级组编程题1:小猫走城堡

准备工作: 导入育最库中的“Castle 3 小猫从坐标点(-165&#xff0c;-93)出发向城堡走去。随着位置的移动&#xff0c;角色大小逐渐变小&#xff0c;最后在城堡前消失。注意1.角色大小在逐渐变化&#xff0c;运行结束再次点击绿旗&#xff0c;程序应还能再次扶行。2.角色应该是…

云原生之API网关Traefik

1. 前言 说到web服务的开源网关&#xff0c;我首先想到的是Nginx&#xff0c;最早使用的就是它&#xff0c;现在都还在使用它。系统上线了Docker Swarm集群之后&#xff0c;不继续使用Nginx直接做Docker服务的网关&#xff0c;是因为Nginx毕竟比Docker Swarm出现的早&#xff0…