如何实现原子加法,但不超过 x?

huangapple go评论88阅读模式
英文:

how to implement atomic add but not exceed x?

问题

我想要在纯原子操作中实现以下的add函数:

var num int
func add(max int) int {
  if num < max {
    num++
  }
  return num
}

我尝试了一个版本:

func add(max int64) int64 {
	for {
		old := atomic.LoadInt64(&x)
		if old + 1 < max {
			if atomic.CompareAndSwapInt64(&x, old, old+1) {
				return old+1
			}
		} else {
			return old
		}
	}
}

然而,我猜想可能有一种更好的解决方案,可以减少失败的机会并避免死循环。

英文:

i want to implement add below in pure atomic operation

var num int
func add(max int) int {
  if num &lt; max {
    num++
  }
  return num
}

i have try one edition

func add(max int64) int64 {
	for {
		old := atomic.LoadInt64(&amp;x)
		if old + 1 &lt; max {
			if atomic.CompareAndSwapInt64(&amp;x, old, old+1) {
				return old+1
			}
		} else {
			return old
		}
	}
}

however, i guess there might be a better solution with less fail chance and avoid dead loop

答案1

得分: 1

这是您的算法的修订版本,其中包含了多个goroutine的测试。它通过了带有和不带有Go竞争检测器的测试。

add.go:

package main

import (
    "fmt"
    "runtime"
    "sync"
    "sync/atomic"
)

var x int64 = -42

func add(max int64) int64 {
    for {
        old := atomic.LoadInt64(&x)
        if old >= max {
            return old
        }
        new := old + 1
        if atomic.CompareAndSwapInt64(&x, old, new) {
            return new
        }
    }
}

func main() {
    const max = 123456
    fmt.Println("max:", max)
    fmt.Println("x:  ", x)
    var wg sync.WaitGroup
    procs := runtime.GOMAXPROCS(0)
    for i := 0; i < procs; i++ {
        wg.Add(1)
        go func(max int64) {
            defer wg.Done()
            for {
                if add(max) >= max {
                    return
                }
            }
        }(max)
    }
    wg.Wait()
    fmt.Println("x:  ", x)

}

https://go.dev/play/p/r-qsnyI7tqv

$ go build -race add.go && ./add
max: 123456
x:   -42
x:   123456
$ go build add.go && ./add
max: 123456
x:   -42
x:   123456
英文:

Here's a revised version of your algorithm with a multiple goroutine test. It passes the test with and without the Go race detector.

add.go:

package main

import (
	&quot;fmt&quot;
	&quot;runtime&quot;
	&quot;sync&quot;
	&quot;sync/atomic&quot;
)

var x int64 = -42

func add(max int64) int64 {
	for {
		old := atomic.LoadInt64(&amp;x)
		if old &gt;= max {
			return old
		}
		new := old + 1
		if atomic.CompareAndSwapInt64(&amp;x, old, new) {
			return new
		}
	}
}

func main() {
	const max = 123456
	fmt.Println(&quot;max:&quot;, max)
	fmt.Println(&quot;x:  &quot;, x)
	var wg sync.WaitGroup
	procs := runtime.GOMAXPROCS(0)
	for i := 0; i &lt; procs; i++ {
		wg.Add(1)
		go func(max int64) {
			defer wg.Done()
			for {
				if add(max) &gt;= max {
					return
				}
			}
		}(max)
	}
	wg.Wait()
	fmt.Println(&quot;x:  &quot;, x)

}

https://go.dev/play/p/r-qsnyI7tqv

$ go build -race add.go &amp;&amp; ./add
max: 123456
x:   -42
x:   123456
$ go build add.go &amp;&amp; ./add
max: 123456
x:   -42
x:   123456

答案2

得分: 0

最简单(但不是最高效)的解决方案是使用互斥锁:

var (
    mu  sync.Mutex
    num int64
)

func add(max int64) int64 {
    mu.Lock()
    defer mu.Unlock()

    if num < max {
        num++
    }
    return num
}

请注意,这段代码使用了互斥锁(sync.Mutex)来确保在修改 num 变量时的线程安全性。mu.Lock() 用于获取锁,mu.Unlock() 用于释放锁。在 add 函数中,我们首先获取锁,然后在修改 num 变量之前检查其值是否小于 max。如果是,则将 num 递增。最后,我们释放锁并返回 num 的值。

英文:

The easiest (but not the most efficient) solution would be to use a mutex:

var (
	mu  sync.Mutex
	num int64
)

func add(max int64) int64 {
	mu.Lock()
	defer mu.Unlock()

	if num &lt; max {
		num++
	}
	return num
}

huangapple
  • 本文由 发表于 2021年12月23日 16:11:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/70459327.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定