【问题标题】:Julia set image rendering ruined by concurrencyJulia设置图像渲染被并发破坏
【发布时间】:2016-04-11 14:11:36
【问题描述】:

我将以下代码更改为并发程序。

// Stefan Nilsson 2013-02-27

// This program creates pictures of Julia sets (en.wikipedia.org/wiki/Julia_set).
package main

import (
    "image"
    "image/color"
    "image/png"
    "log"
    "math/cmplx"
    "os"
    "strconv"
)

type ComplexFunc func(complex128) complex128

var Funcs []ComplexFunc = []ComplexFunc{
    func(z complex128) complex128 { return z*z - 0.61803398875 },
    func(z complex128) complex128 { return z*z + complex(0, 1) },
}

func main() {
    for n, fn := range Funcs {
        err := CreatePng("picture-"+strconv.Itoa(n)+".png", fn, 1024)
        if err != nil {
            log.Fatal(err)
        }
    }
}

// CreatePng creates a PNG picture file with a Julia image of size n x n.
func CreatePng(filename string, f ComplexFunc, n int) (err error) {
    file, err := os.Create(filename)
    if err != nil {
        return
    }
    defer file.Close()
    err = png.Encode(file, Julia(f, n))
    return
}

// Julia returns an image of size n x n of the Julia set for f.
func Julia(f ComplexFunc, n int) image.Image {
    bounds := image.Rect(-n/2, -n/2, n/2, n/2)
    img := image.NewRGBA(bounds)
    s := float64(n / 4)
    for i := bounds.Min.X; i < bounds.Max.X; i++ {
        for j := bounds.Min.Y; j < bounds.Max.Y; j++ {
            n := Iterate(f, complex(float64(i)/s, float64(j)/s), 256)
            r := uint8(0)
            g := uint8(0)
            b := uint8(n % 32 * 8)
            img.Set(i, j, color.RGBA{r, g, b, 255})
        }
    }
    return img
}

// Iterate sets z_0 = z, and repeatedly computes z_n = f(z_{n-1}), n ≥ 1,
// until |z_n| > 2  or n = max and returns this n.
func Iterate(f ComplexFunc, z complex128, max int) (n int) {
    for ; n < max; n++ {
        if real(z)*real(z)+imag(z)*imag(z) > 4 {
            break
        }
        z = f(z)
    }
    return
}

我决定尝试使Julia() 函数并发。所以我把它改成:

func Julia(f ComplexFunc, n int) image.Image {  
    bounds := image.Rect(-n/2, -n/2, n/2, n/2)  
    img := image.NewRGBA(bounds)                
    s := float64(n / 4)                          
    for i := bounds.Min.X; i < bounds.Max.X; i++ {
        for j := bounds.Min.Y; j < bounds.Max.Y; j++ {
            go func(){
                n := Iterate(f, complex(float64(i)/s, float64(j)/s), 256)
                r := uint8(0)   
                g := uint8(0)   
                b := uint8(n % 32 * 8)  
                img.Set(i, j, color.RGBA{r, g, b, 255}) 
            }()
        }
    }
    return img 

此更改导致图像看起来非常不同。图案基本相同,但有很多以前没有的白色像素。

这里发生了什么?

【问题讨论】:

    标签: image go concurrency rendering


    【解决方案1】:

    有2个问题:

    1. 您实际上并没有等待您的 goroutine 完成。
    2. 您不会将 i and j 传递给 goroutine,因此它们几乎总是最后的 i 和 j。

    您的函数应该类似于:

    func Julia(f ComplexFunc, n int) image.Image {
        var wg sync.WaitGroup
        bounds := image.Rect(-n/2, -n/2, n/2, n/2)
        img := image.NewRGBA(bounds)
        s := float64(n / 4)
        for i := bounds.Min.X; i < bounds.Max.X; i++ {
            for j := bounds.Min.Y; j < bounds.Max.Y; j++ {
                wg.Add(1)
                go func(i, j int) {
                    n := Iterate(f, complex(float64(i)/s, float64(j)/s), 256)
                    r := uint8(0)
                    g := uint8(0)
                    b := uint8(n % 32 * 8)
                    img.Set(i, j, color.RGBA{r, g, b, 255})
                    wg.Done()
                }(i, j)
            }
        }
        wg.Wait()
        return img
    }
    

    一个额外提示,在深入研究并发性时,使用race detector 尝试您的代码通常是个好主意。

    可能必须使用互斥锁来调用 img.Set,但我不太确定,也无法测试 atm。

    【讨论】:

    • 不需要互斥体
    猜你喜欢
    • 1970-01-01
    • 2019-08-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多