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

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

时间:2023-08-14 20:44:26浏览次数:47  
标签:taskRunCount testing TestGoPoolWithRetry Golang go 玩转 GPT gopool

目录

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 的介绍可以跳转这篇文章:

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

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

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

在这个 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 感兴趣,可以跳转阅读这篇文章:

3.1 和 GPT-4 的第一轮沟通

  • 我将问题抛给了 DevChat:

  • 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 的回答如下:

  • 文字版:

是的,使用原子操作可以更简洁地解决这个问题,而且性能也会更好。我们可以使用 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:

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

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

4. 总结

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

但是,我不喜欢按理说。

拜拜,我下班了。

明天见。

bye……

标签:taskRunCount,testing,TestGoPoolWithRetry,Golang,go,玩转,GPT,gopool
From: https://www.cnblogs.com/daniel-hutao/p/devchat-gpt-in-5min-6-race-detector.html

相关文章

  • Golang之旅——Defer
    defer首先来看一下官方的解析:Adeferredfunction’sargumentsareevaluatedwhenthedeferstatementisevaluated.DeferredfunctioncallsareexecutedinLastInFirstOutorderafterthesurroundingfunctionreturns.Deferredfunctionsmayreadandassign......
  • Golang 内嵌静态资源-转
    转载:https://www.mousemin.com/archives/go-embed-resource/把静态资源嵌入在程序里,原因无外乎以下几点:布署程序更简单。传统部署要么需要把静态资源和编译好的程序一起打包上传,要么使用docker和dockerfile自动化.保证程序完整性。运行中发生静态资源损坏或丢失往往会影响程......
  • Golang: 使用embed内嵌资源文件-转
    转载:https://blog.kakkk.net/archives/71/embed介绍首先,embed是 go1.16才有的新特性,使用方法非常简单,通过 //go:embed指令,在打包时将文件内嵌到程序中。官方文档:https://pkg.go.dev/embed快速开始文件结构.├──go.mod├──main.go└──resources└──hello......
  • Python Chagpt Ai智能批量问答的研究实现
    首先介绍一下我的软件界面:功能介绍:1:实现了单个一问一答功能    2:可以批量将本地执行目录下的Excel中的问题向chatgpt提问题,系统得到答案后批量将问题对应的答案写入到Excel中 部分代码截图:  有感兴趣的联系V:xunmengqishi ......
  • 一文玩转MQTT (ESP8266 DHT11 MQTT MYSQL方案)
    本文我们来聊一聊esp8266利用mqtt协议进行通信。并将数据数据存入数据库的操作。关于MQTTMQTT(消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的“轻量级”通讯协议,MQTT最大优点在于,用极少的代码和有限的带宽,为连接远程设备提供实时可靠的消息服务。搭建MQTT服务器......
  • 智能驾驶系列报告之一:智能驾驶 ChatGPT时刻有望来临
    原创|文BFT机器人L3功能加速落地,政策标准有望明确L2发展日益成熟,L3功能加速落地。根据市场监管总局发布的《汽车驾驶自动化分级》与SAE发布的自动驾驶分级标准,自动驾驶主要分为6个级别(0级到5级,L0到L5)。从车辆控制、目标与事件检测、任务接管等层面看,国标与SAE分级......
  • [完结8章]程序员的 AI 启蒙课,ChatGPT 辅助开发 Vue3 项目
    点击下载:程序员的AI启蒙课:ChatGPT让你1人顶3人提取码:8zwd Vue是一款用于构建用户界面的JavaScript框,它基于标准的HTML、CSS和JavaScript构建,并提供了一套声明式的、组件化的编程模型,用以帮助开发者高效地开发用户界面。目前,Vue3.0正式版也发布了两年的时间,越......
  • 音频创作的新境界——AudioGPT服务介绍
    音频在数字时代扮演着重要角色,而现在有了AudioGPT服务,音频创作将迎来新的突破。AudioGPT是一种新型的自然语言处理模型,它不仅可以理解和生成语音、音乐、声音等音频形式,还可以生成与音频相对应的说话头像。本文将介绍AudioGPT服务的作用及其对音频创作的影响。1.AudioGPT服务简介......
  • 玩转Linux必不可少的命令之tar
    ❝在linux操作中,我们经常对文件进行操作,比如移动、复制等。单个文件的时候很好操作,但是一但文件比较多的情况下,我们最好的办法就是压缩打包,而tar命令可以帮助我们实现打包、解压的操作!❞什么是Tar它是Unix和类Unix系统上的压缩打包工具,可以将多个文件合并为一个文件,打包后的文件后......
  • 一种基于ChatGPT的高效吃瓜方式的探索和研究。
    你好呀,我是歪歪。最近掌握了一个新的吃瓜方式,我觉得还行,给大家简单分享一下。事情说来就话长了,还得从最近的一次“工业革命”开始,也就是从超导材料说起。8月1日的时候B站这个视频引爆网络:随后“常温常压下的超导材料”直接霸占了各个新闻媒体的头条,引爆了话题,点燃了股市......