让 GPT-4 来修复 Golang “数据竞争”问题 - 每天5分钟玩转 GPT 编程系列(6)

这篇具有很好参考价值的文章主要介绍了让 GPT-4 来修复 Golang “数据竞争”问题 - 每天5分钟玩转 GPT 编程系列(6)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录
  • 1. Golang 中的“数据竞争”
  • 2. GoPool 中的数据竞争问题
  • 3. 让 GPT-4 来修复数据竞争问题
    • 3.1 和 GPT-4 的第一轮沟通
    • 3.2 和 GPT-4 的第二轮沟通
    • 3.3 提交代码
  • 4. 总结

1. Golang 中的“数据竞争”

我在上个月发过一篇《跟着 GPT-4 从0到1学习 Golang 并发机制(三)》,文中有一节专门介绍了“Race Detector 检测数据竞争”。

数据竞争发生在当两个或更多的 goroutine 并发访问同一块内存区域,且至少有一个访问是写入操作时。比如这段代码:

package main

import (
	"fmt"
)

var counter int

func increment() {
	counter++
}

func main() {
	go increment()
	go increment()
	fmt.Println(counter)
}

在这个程序中,两个 goroutine 都在尝试增加 counter 的值。这会导致数据竞争,因为 counter++ 不是一个原子操作。

如果你使用 Race Detector 来运行这个程序(go run -race main.go),它会报告数据竞争,并给出详细的报告,包括数据竞争发生的位置和涉及的 goroutine。

2. GoPool 中的数据竞争问题

又聊到 GoPool 了。没错,还是那个宣称自己性能全网第一的 Golang Worker Pool 实现。一个普通程序员拿着 GPT-4 只花了3天就肝出来的一个高性能 Worker 池。

关于 GoPool 的介绍可以跳转这篇文章:

  • 《仅三天,我用 GPT-4 生成了性能全网第一的 Golang Worker Pool,轻松打败 GitHub 万星项目》

昨天就有人提出 GoPool 中可能存在 goroutine 泄露问题:

  • adjustWorkers is not notified on exit, thus potential resource leak

那位社区贡献者还提了一个 PR,然后为此我专门写了一篇文章来详细介绍这个 issue 的“前世今生”:

  • 《让 GPT-4 来 review 开源社区贡献者的 PR》

今天又有人提出了 GoPool 的测试用例中存在“数据竞争”问题:

  • execute go test -race failed

在这个 issue 中贡献者贴了这样一段日志:

$ go test -v -race ./...
=== RUN   TestGoPoolWithMutex
--- PASS: TestGoPoolWithMutex (0.11s)
=== RUN   TestGoPoolWithSpinLock
--- PASS: TestGoPoolWithSpinLock (0.11s)
=== RUN   TestGoPoolWithError
--- PASS: TestGoPoolWithError (0.11s)
=== RUN   TestGoPoolWithResult
--- PASS: TestGoPoolWithResult (0.11s)
=== RUN   TestGoPoolWithRetry
==================
WARNING: DATA RACE
Read at 0x00c00001c258 by goroutine 423:
  github.com/devchat-ai/gopool.TestGoPoolWithRetry()
      /workspaces/gopool/gopool_test.go:147 +0x284
  testing.tRunner()
      /usr/local/go/src/testing/testing.go:1576 +0x216
  testing.(*T).Run.func1()
      /usr/local/go/src/testing/testing.go:1629 +0x47

Previous write at 0x00c00001c258 by goroutine 523:
  github.com/devchat-ai/gopool.TestGoPoolWithRetry.func1()
      /workspaces/gopool/gopool_test.go:138 +0x64
  github.com/devchat-ai/gopool.(*worker).executeTaskWithoutTimeout()
      /workspaces/gopool/worker.go:78 +0xd1
  github.com/devchat-ai/gopool.(*worker).executeTask()
      /workspaces/gopool/worker.go:41 +0xc7
  github.com/devchat-ai/gopool.(*worker).start.func1()
      /workspaces/gopool/worker.go:26 +0xaa

Goroutine 423 (running) created at:
  testing.(*T).Run()
      /usr/local/go/src/testing/testing.go:1629 +0x805
  testing.runTests.func1()
      /usr/local/go/src/testing/testing.go:2036 +0x8d
  testing.tRunner()
      /usr/local/go/src/testing/testing.go:1576 +0x216
  testing.runTests()
      /usr/local/go/src/testing/testing.go:2034 +0x87c
  testing.(*M).Run()
      /usr/local/go/src/testing/testing.go:1906 +0xb44
  main.main()
      _testmain.go:61 +0x2e9

Goroutine 523 (running) created at:
  github.com/devchat-ai/gopool.(*worker).start()
      /workspaces/gopool/worker.go:23 +0xf7
  github.com/devchat-ai/gopool.NewGoPool()
      /workspaces/gopool/gopool.go:75 +0x54f
  github.com/devchat-ai/gopool.TestGoPoolWithRetry()
      /workspaces/gopool/gopool_test.go:134 +0xfb
  testing.tRunner()
      /usr/local/go/src/testing/testing.go:1576 +0x216
  testing.(*T).Run.func1()
      /usr/local/go/src/testing/testing.go:1629 +0x47
==================
    testing.go:1446: race detected during execution of test
--- FAIL: TestGoPoolWithRetry (0.10s)
=== NAME  
    testing.go:1446: race detected during execution of test
FAIL
FAIL    github.com/devchat-ai/gopool    0.558s
FAIL

啥?又有 issue?那我得立刻马上现在就把它“秒”掉!

3. 让 GPT-4 来修复数据竞争问题

问题很明确了,最大的问题是“日志有点长”,次要问题是“我有点懒”。没错,我是闭口不提如何从日志中寻找“哪里少加了锁导致数据竞争”。

我决定将这个问题抛给 GPT-4,让 GPT-4 直接定位问题,修复问题,再帮我写好 commit message,一条龙服务。

下文使用 DevChat 和 GPT-4 交互。如果你对 DevChat 感兴趣,可以跳转阅读这篇文章:

  • 《DevChat:将 GPT-4 无缝融入 VS Code,极致提升你的编程体验》
  • 如果你想要注册 DevChat,点击这个链接可以免费注册,目前 DevChat 还在公测中,不收费。

3.1 和 GPT-4 的第一轮沟通

  • 我将问题抛给了 DevChat:

让 GPT-4 来修复 Golang “数据竞争”问题 - 每天5分钟玩转 GPT 编程系列(6)

  • DevChat 给出的回答如下:

  • The user is running tests on a Go project and encountered a data race error.
  • The error is occurring in the TestGoPoolWithRetry function in the gopool_test.go file.
  • The data race is happening between two goroutines, one reading and the other writing to the same memory address.
  • The reading is happening in the TestGoPoolWithRetry function, and the writing is happening in the TestGoPoolWithRetry.func1 function, which is called by the executeTaskWithoutTimeout function in the worker.go file.

To fix this issue, we need to ensure that the taskRunCount variable is accessed in a thread-safe manner. We can achieve this by using a mutex to lock the variable when it's being read or written. Here is the modified TestGoPoolWithRetry function:

func TestGoPoolWithRetry(t *testing.T) {
    var retryCount = 3
    var taskError = errors.New("task error")
    var taskRunCount = 0
    var mu sync.Mutex

    pool := NewGoPool(100, WithRetryCount(retryCount))
    defer pool.Release()

    pool.AddTask(func() (interface{}, error) {
        mu.Lock()
        taskRunCount++
        mu.Unlock()
        if taskRunCount <= retryCount {
            return nil, taskError
        }
        return nil, nil
    })

    pool.Wait()

    mu.Lock()
    if taskRunCount != retryCount+1 {
        t.Errorf("Expected task to run %v times, but it ran %v times", retryCount+1, taskRunCount)
    }
    mu.Unlock()
}

In this modification, we introduced a mutex mu and used it to lock taskRunCount when it's being incremented and read. This ensures that only one goroutine can access taskRunCount at a time, preventing the data race.


因为这轮聊天我发送的内容都是英文的,所以 GPT-4 也就“傲娇”地全英文响应了。扫一眼这段代码不难发现 GPT-4 通过在 taskRunCount 操作前后加互斥锁来解决“数据竞争问题。

3.2 和 GPT-4 的第二轮沟通

我觉得一个小小的 taskRunCount 数据保护,用原子操作似乎更简洁;于是我又对 DevChat 说:

是不是改成原子操作会更简洁?

  • DevChat 的回答如下:

让 GPT-4 来修复 Golang “数据竞争”问题 - 每天5分钟玩转 GPT 编程系列(6)

  • 文字版:

是的,使用原子操作可以更简洁地解决这个问题,而且性能也会更好。我们可以使用 sync/atomic 包中的 AddInt32LoadInt32 函数来实现。这里是修改后的 TestGoPoolWithRetry 函数:

import "sync/atomic"

func TestGoPoolWithRetry(t *testing.T) {
    var retryCount = int32(3)
    var taskError = errors.New("task error")
    var taskRunCount int32 = 0

    pool := NewGoPool(100, WithRetryCount(int(retryCount)))
    defer pool.Release()

    pool.AddTask(func() (interface{}, error) {
        atomic.AddInt32(&taskRunCount, 1)
        if taskRunCount <= retryCount {
            return nil, taskError
        }
        return nil, nil
    })

    pool.Wait()

    if atomic.LoadInt32(&taskRunCount) != retryCount+1 {
        t.Errorf("Expected task to run %v times, but it ran %v times", retryCount+1, taskRunCount)
    }
}

在这个修改中,我们使用 atomic.AddInt32 来原子地增加 taskRunCount,并使用 atomic.LoadInt32 来原子地读取 taskRunCount。这样可以确保在并发环境下对 taskRunCount 的访问是线程安全的。

3.3 提交代码

这个修改不算复杂,GPT-4 也是一遍梭哈,目测没问题(当然手测也没有问题)。于是我继续让 GPT-4 写了一个 commit message:

让 GPT-4 来修复 Golang “数据竞争”问题 - 每天5分钟玩转 GPT 编程系列(6)

就这样,我提交了这个 PR:

  • Refactor TestGoPoolWithRetry to use atomic operations

点开看看呗,这个 PR 里的“英文描述”还是挺地道的呢!是不是给人一种“我英文还不错”的错觉?哇咔咔,GPT 在手,你都分辨不了我的母语是汉语还是英语。

4. 总结

按理说,这里需要有一个总结。

但是,我不喜欢按理说。

拜拜,我下班了。

明天见。

bye……文章来源地址https://www.toymoban.com/news/detail-647449.html

到了这里,关于让 GPT-4 来修复 Golang “数据竞争”问题 - 每天5分钟玩转 GPT 编程系列(6)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【数据结构】每天五分钟,快速入门数据结构(二)——链表

    目录 一 构建一个单向链表 二 特点 三 时间复杂度 四 相关算法 1.判断链表是否成环及成环位置 2.链表反转 五 Java中的LinkedList 类 1.使用 2.LinkedList 方法 长度无限 适合任意位置插入和删除频繁的场景 物理上可以不连续 访问、插入、删除 的时间复杂度均为O(n) 在头部插入元素

    2024年02月21日
    浏览(45)
  • 《每天十分钟》-红宝书第4版-语言基础-数据类型(五)

    这个符号作为一个属性表示“一个布尔值,如果是 true,则意味着对象应 该用 Array.prototype.concat()打平其数组元素”。ES6 中的 Array.prototype.concat()方法会 根 据 接 收 到 的 对 象 类 型 选 择 如 何 将 一 个 类 数 组 对 象 拼 接 成 数 组 实 例 。 覆 盖 Symbol.isConcat- Spreadable 的值可

    2024年02月14日
    浏览(40)
  • 《每天十分钟》-红宝书第4版-语言基础-数据类型(一)

    关于ECMAScript 数据类型,“非常6+1” 6:六种简单数据类型(也称为原始类型) Undefined Null Boolean Number String Symbol(ES6新增) 1:一种复杂数据类型 Object 使用 typeof 操作符 (注意是操作符)可以判断一个变量的数据类型 \\\"undefined\\\"表示值未定义; \\\"boolean\\\"表示值为布尔值; \\\"string\\\"表示

    2024年02月13日
    浏览(39)
  • [阿里云] 10分钟带你玩转阿里云ECS和云盘 (大数据上云必备)

    前言 由于准备做一些离线计算和实时计算的模拟, 发现某些教程内的阿里云还挺好用的, 在这里把相关的经验分享给大家. 简单的心路历程: 起先笔者搭建了一套本地集群. 但是后来发现, 因为没用网络IP的反穿, 本地的集群的网络访问非常不便. 其次, 集群的启停, 网络和磁盘管

    2024年02月02日
    浏览(49)
  • Failed to load steamui.dll问题与解决方法详解,3分钟教你修复steamui.dll文件

    我们运行Steam客户端时,有时可能会遇到一个错误提示,称为“Failed to load steamui.dll”。这种情况对于任何想要使用Steam服务的玩家来说都是一种麻烦。那么,具体是什么意思呢?出现这个问题的原因又是什么呢?又该如何解决这个问题呢?这篇文章将详细介绍。 \\\"Failed to loa

    2024年02月04日
    浏览(187)
  • 每天十分钟学会Spark

    Spark是什么 Spark是一种基于内存的快速、通用、可拓展的大数据分析计算引擎。 Spark官网:http://spark.apache.org/ Spark的特点 1、快速   一般情况下,对于迭代次数较多的应用程序,Spark程序在内存中的运行速度是Hadoop MapReduce运行速度的100多倍,在磁盘上的运行速度是Hadoop MapRedu

    2024年03月18日
    浏览(61)
  • 带你【玩转Linux命令】➾ find & cut 每天2个day06

    1.1 find-查找文件或目录 📖 find命令用于查找符合条件的文件,任何在参数之前的字符串都将视为欲查找的目录。假如没有指定目录,则会查找当前的目录,假如没有设定参数,则会以“-print”参数作为默认值。 当指定参数时,可在参数之前加上“l”,表示查找不符合此参数

    2024年02月16日
    浏览(33)
  • 带你【玩转Linux命令】➽ cat & chattr 每天2个day01

    1.1 cat-打印输出文件内容 📖 cat 命令用于连接文件并打印到标准输出设备上。 若不指定任何文件名称,或是指定的文件名为“-”,则cat命会从标准输人设备读取数据(例如键盘),然后再把所得到的数据输出到输出设备也可运用shell的特殊字符“”和“”,把多个文件的内容合

    2024年02月15日
    浏览(42)
  • 每天五分钟计算机视觉:揭秘迁移学习

    随着人工智能的迅速发展,深度学习已经成为了许多领域的关键技术。然而,深度学习模型的训练需要大量的标注数据,这在很多情况下是不现实的。迁移学习作为一种有效的方法,可以在已有的数据和模型上进行训练,然后将其应用于新的任务。这种方法大大降低了对新任

    2024年01月24日
    浏览(51)
  • 每天五分钟机器学习:神经网络模型参数的选择

    在深度学习和人工智能的浪潮中,神经网络作为其中的核心力量,发挥着举足轻重的作用。然而,神经网络的性能并非一蹴而就,而是需要经过精心的参数选择和调优。 神经网络由大量的神经元组成,每个神经元之间通过权重进行连接。这些权重,以及神经元的偏置、激活函

    2024年04月28日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包