Julia集图像渲染被并发破坏了。

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

Julia set image rendering ruined by concurrency

问题

这段代码的变化导致图像看起来非常不同。图案基本相同,但是出现了许多之前不存在的白色像素。

这是因为你在Julia()函数中使用了并发操作。通过使用go关键字创建了一个匿名函数作为goroutine,在每个像素点上都启动了一个goroutine来计算对应的n值并设置像素颜色。

然而,这种并发操作可能导致竞争条件。由于goroutine的执行是异步的,它们可能会同时访问和修改共享的img对象。这可能导致图像数据的不一致性,从而产生了额外的白色像素。

为了解决这个问题,你可以使用互斥锁(mutex)来保护对img对象的并发访问。在每个goroutine中,使用互斥锁来确保每次只有一个goroutine能够修改img对象。这样可以避免竞争条件,保证图像数据的一致性。

另外,你还需要在主函数中等待所有的goroutine执行完毕,可以使用sync.WaitGroup来实现。在每个goroutine启动之前,调用wg.Add(1)增加等待组的计数器,然后在goroutine结束时调用wg.Done()减少计数器。最后,使用wg.Wait()等待所有的goroutine执行完毕。

下面是修改后的代码示例:

  1. import (
  2. "sync"
  3. )
  4. func Julia(f ComplexFunc, n int) image.Image {
  5. bounds := image.Rect(-n/2, -n/2, n/2, n/2)
  6. img := image.NewRGBA(bounds)
  7. s := float64(n / 4)
  8. var wg sync.WaitGroup
  9. var mutex sync.Mutex
  10. for i := bounds.Min.X; i < bounds.Max.X; i++ {
  11. for j := bounds.Min.Y; j < bounds.Max.Y; j++ {
  12. wg.Add(1)
  13. go func(i, j int) {
  14. defer wg.Done()
  15. n := Iterate(f, complex(float64(i)/s, float64(j)/s), 256)
  16. r := uint8(0)
  17. g := uint8(0)
  18. b := uint8(n % 32 * 8)
  19. mutex.Lock()
  20. img.Set(i, j, color.RGBA{r, g, b, 255})
  21. mutex.Unlock()
  22. }(i, j)
  23. }
  24. }
  25. wg.Wait()
  26. return img
  27. }

通过添加互斥锁和等待组,你可以确保并发操作的安全性,并消除额外的白色像素。

英文:

I have the following code that I am to change into a concurrent program.

  1. // Stefan Nilsson 2013-02-27
  2. // This program creates pictures of Julia sets (en.wikipedia.org/wiki/Julia_set).
  3. package main
  4. import (
  5. &quot;image&quot;
  6. &quot;image/color&quot;
  7. &quot;image/png&quot;
  8. &quot;log&quot;
  9. &quot;math/cmplx&quot;
  10. &quot;os&quot;
  11. &quot;strconv&quot;
  12. )
  13. type ComplexFunc func(complex128) complex128
  14. var Funcs []ComplexFunc = []ComplexFunc{
  15. func(z complex128) complex128 { return z*z - 0.61803398875 },
  16. func(z complex128) complex128 { return z*z + complex(0, 1) },
  17. }
  18. func main() {
  19. for n, fn := range Funcs {
  20. err := CreatePng(&quot;picture-&quot;+strconv.Itoa(n)+&quot;.png&quot;, fn, 1024)
  21. if err != nil {
  22. log.Fatal(err)
  23. }
  24. }
  25. }
  26. // CreatePng creates a PNG picture file with a Julia image of size n x n.
  27. func CreatePng(filename string, f ComplexFunc, n int) (err error) {
  28. file, err := os.Create(filename)
  29. if err != nil {
  30. return
  31. }
  32. defer file.Close()
  33. err = png.Encode(file, Julia(f, n))
  34. return
  35. }
  36. // Julia returns an image of size n x n of the Julia set for f.
  37. func Julia(f ComplexFunc, n int) image.Image {
  38. bounds := image.Rect(-n/2, -n/2, n/2, n/2)
  39. img := image.NewRGBA(bounds)
  40. s := float64(n / 4)
  41. for i := bounds.Min.X; i &lt; bounds.Max.X; i++ {
  42. for j := bounds.Min.Y; j &lt; bounds.Max.Y; j++ {
  43. n := Iterate(f, complex(float64(i)/s, float64(j)/s), 256)
  44. r := uint8(0)
  45. g := uint8(0)
  46. b := uint8(n % 32 * 8)
  47. img.Set(i, j, color.RGBA{r, g, b, 255})
  48. }
  49. }
  50. return img
  51. }
  52. // Iterate sets z_0 = z, and repeatedly computes z_n = f(z_{n-1}), n &#226;‰&#165; 1,
  53. // until |z_n| &gt; 2 or n = max and returns this n.
  54. func Iterate(f ComplexFunc, z complex128, max int) (n int) {
  55. for ; n &lt; max; n++ {
  56. if real(z)*real(z)+imag(z)*imag(z) &gt; 4 {
  57. break
  58. }
  59. z = f(z)
  60. }
  61. return
  62. }

I have decided to try and make the Julia() function concurrent. So I changed it to:

  1. func Julia(f ComplexFunc, n int) image.Image {
  2. bounds := image.Rect(-n/2, -n/2, n/2, n/2)
  3. img := image.NewRGBA(bounds)
  4. s := float64(n / 4)
  5. for i := bounds.Min.X; i &lt; bounds.Max.X; i++ {
  6. for j := bounds.Min.Y; j &lt; bounds.Max.Y; j++ {
  7. go func(){
  8. n := Iterate(f, complex(float64(i)/s, float64(j)/s), 256)
  9. r := uint8(0)
  10. g := uint8(0)
  11. b := uint8(n % 32 * 8)
  12. img.Set(i, j, color.RGBA{r, g, b, 255})
  13. }()
  14. }
  15. }
  16. return img

This change causes the images to look very different. The patterns are essentially the same, but there are a lot of white pixels that were not there before.

What is happening here?

答案1

得分: 0

有两个问题:

  1. 你实际上没有等待 goroutine 完成。
  2. 你没有将 ij 传递给 goroutine,所以它们几乎总是最后的 ij

你的函数应该类似于:

  1. func Julia(f ComplexFunc, n int) image.Image {
  2. var wg sync.WaitGroup
  3. bounds := image.Rect(-n/2, -n/2, n/2, n/2)
  4. img := image.NewRGBA(bounds)
  5. s := float64(n / 4)
  6. for i := bounds.Min.X; i < bounds.Max.X; i++ {
  7. for j := bounds.Min.Y; j < bounds.Max.Y; j++ {
  8. wg.Add(1)
  9. go func(i, j int) {
  10. n := Iterate(f, complex(float64(i)/s, float64(j)/s), 256)
  11. r := uint8(0)
  12. g := uint8(0)
  13. b := uint8(n % 32 * 8)
  14. img.Set(i, j, color.RGBA{r, g, b, 255})
  15. wg.Done()
  16. }(i, j)
  17. }
  18. }
  19. wg.Wait()
  20. return img
  21. }

一个额外的提示,在并发编程中,通常使用 race detector 来测试你的代码是一个好主意。

可能需要使用互斥锁来调用 img.Set,但我不太确定,而且我现在无法测试。

英文:

There are 2 problems:

  1. You don't actually wait for your goroutines to finish.
  2. You don't pass i and j to the goroutine, so they will almost always be the last i and j.

Your function should look something like:

  1. func Julia(f ComplexFunc, n int) image.Image {
  2. var wg sync.WaitGroup
  3. bounds := image.Rect(-n/2, -n/2, n/2, n/2)
  4. img := image.NewRGBA(bounds)
  5. s := float64(n / 4)
  6. for i := bounds.Min.X; i &lt; bounds.Max.X; i++ {
  7. for j := bounds.Min.Y; j &lt; bounds.Max.Y; j++ {
  8. wg.Add(1)
  9. go func(i, j int) {
  10. n := Iterate(f, complex(float64(i)/s, float64(j)/s), 256)
  11. r := uint8(0)
  12. g := uint8(0)
  13. b := uint8(n % 32 * 8)
  14. img.Set(i, j, color.RGBA{r, g, b, 255})
  15. wg.Done()
  16. }(i, j)
  17. }
  18. }
  19. wg.Wait()
  20. return img
  21. }

A bonus tip, when diving into concurrency, it's usually a good idea to try your code with the race detector.

You might have to use a mutex to call img.Set but I'm not very sure and I can't test atm.

huangapple
  • 本文由 发表于 2016年4月11日 22:11:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/36551154.html
匿名

发表评论

匿名网友

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

确定