Golang多个goroutine通过引用共享同一变量

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

Golang multiple goroutine sharing same variable by reference

问题

我正在尝试运行多个goroutine,这些goroutine通过引用传递修改同一个变量。

但是我确定我实现的方式在功能上是不正确的。尽管在我的测试中似乎工作正常,但我有一种感觉,如果第二个goroutine的运行时间比第一个长得多,这种模式会导致父函数在第一个goroutine完成时结束。

我想听听您的意见/建议/建议。

package auth

import (
	"regexp"

	zxcvbn "github.com/nbutton23/zxcvbn-go"
	"golang.org/x/net/context"
)

type AuthService struct{}

func NewAuthService() *AuthService {
	return &AuthService{}
}

func (this *AuthService) ValidateCredentials(ctx context.Context, req *ValidateCredentialsRequest) (*ValidateCredentialsResponse, error) {
	c := make(chan *ValidateCredentialsResponse)

	go validatePassword(req.GetPassword(), c)
	go validateUsername(req.GetUsername(), c)

	c <- &ValidateCredentialsResponse{IsValid: true}

	return <-c, nil
}

func validateUsername(email string, c chan *ValidateCredentialsResponse) {
	for {
		res := <-c

		if email == "" {
			res.IsValid = false
			res.Username = "请提供您的电子邮件地址。"
		} else if len(email) > 128 {
			res.IsValid = false
			res.Username = "电子邮件地址不能超过128个字符。"
		} else if !regexp.MustCompile(`.+@.+`).MatchString(email) {
			res.IsValid = false
			res.Username = "请输入有效的电子邮件地址。"
		}

		c <- res
	}
}

func validatePassword(password string, c chan *ValidateCredentialsResponse) {
	for {
		res := <-c

		if password == "" {
			res.IsValid = false
			res.Password = "请提供您的密码。"
		} else {
			quality := zxcvbn.PasswordStrength(password, []string{})
			if quality.Score < 3 {
				res.IsValid = false
				res.Password = "您的密码太弱。"
			}
		}

		c <- res
	}
}

以上是您提供的代码的翻译。

英文:

I am trying to run multiple goroutines that modify the same variable passed by reference.

But I am sure the way I have implemented this is functionally incorrect. Even though it seems to be working in my tests, I have a feeling this pattern would end the parent function when the first goroutine completes if the second goroutine takes considerably longer to run than the first one.

I would like your input/suggestions/advice.

package auth
import (
&quot;regexp&quot;
zxcvbn &quot;github.com/nbutton23/zxcvbn-go&quot;
&quot;golang.org/x/net/context&quot;
)
type AuthService struct{}
func NewAuthService() *AuthService {
return &amp;AuthService{}
}
func (this *AuthService) ValidateCredentials(ctx context.Context, req *ValidateCredentialsRequest) (*ValidateCredentialsResponse, error) {
c := make(chan *ValidateCredentialsResponse)
go validatePassword(req.GetPassword(), c)
go validateUsername(req.GetUsername(), c)
c &lt;- &amp;ValidateCredentialsResponse{IsValid: true}
return &lt;-c, nil
}
func validateUsername(email string, c chan *ValidateCredentialsResponse) {
for {
res := &lt;-c
if email == &quot;&quot; {
res.IsValid = false
res.Username = &quot;Please provide your email address.&quot;
} else if len(email) &gt; 128 {
res.IsValid = false
res.Username = &quot;Email address can not exceed 128 characters.&quot;
} else if !regexp.MustCompile(`.+@.+`).MatchString(email) {
res.IsValid = false
res.Username = &quot;Please enter a valid email address.&quot;
}
c &lt;- res
}
}
func validatePassword(password string, c chan *ValidateCredentialsResponse) {
for {
res := &lt;-c
if password == &quot;&quot; {
res.IsValid = false
res.Password = &quot;Please provide your password.&quot;
} else {
quality := zxcvbn.PasswordStrength(password, []string{})
if quality.Score &lt; 3 {
res.IsValid = false
res.Password = &quot;Your password is weak.&quot;
}
}
c &lt;- res
}
}

答案1

得分: 2

你确定需要使用goroutines来执行简单的验证吗?无论如何,你编写的代码使用了goroutines,但它们并没有并行运行。

你的代码中发生了什么:
你创建了一个非缓冲通道,并将CredentialResponse变量放入其中。
然后,两个goroutine中的任意一个从通道中读取变量,执行一些操作,然后将变量放回通道。
当第一个goroutine执行某些操作时,第二个goroutine只是在等待从通道中接收值。

所以你的代码使用了goroutines,但很难称之为并行。

如果你需要一些耗时的操作来验证数据,比如IO操作或CPU操作,你可能希望使用goroutines,但在CPU操作的情况下,你需要指定GOMAXPROCS>1才能获得一些性能提升。

如果我想要使用goroutines进行验证,我会写出类似这样的代码:

func validateCredentials(req *ValidateCredentialsRequest) {
    ch := make(chan bool, 2)
    go func(name string) {
        // ... 验证代码
        ch <- true // 或者 false
    }(req.GetUsername())

    go func(pwd string) {
        // ... 验证代码
        ch <- true // 或者 false
    }(req.GetPassword())

    valid := true
    for i := 0; i < 2; i++ {
        v := <-ch
        valid = valid && v
    }

    // ...
}
英文:

Are you sure you need goroutines to perform simple validations?
Anyway the code you have written uses goroutines, but they are not running in parallel.

What's going on in your code:
you create non-buffered channel and put CredentialResponse variable into it.
Then one goroutine (any of two) reads variable from channel, performs some actions, and puts variable back to the channel.
While first goroutine was doing some actions, second one was just waiting for a value from a channel.

So your code uses goroutines, but it can hardly be called parallel.

You may want to use goroutines if you need some heavy operations to validate data: io ops, or CPU, but in case of CPU you need specify GOMAXPROCS>1 to get some performance gain.

If I'd wanted to use goroutines for validation, I'd have written smth like it:

func validateCredentials(req *ValidateCredentialsRequest){
ch := make(chan bool, 2)
go func(name string){
// ... validation code
ch &lt;- true // or false
}(req.GetUsername())
go func(pwd string){
// ... validation code
ch &lt;- true // or false
}(req.GetPassword())
valid := true
for i := 0; i &lt; 2; i++ {
v := &lt;- result
valid = valid &amp;&amp; v
}
// ...
}

huangapple
  • 本文由 发表于 2017年2月5日 09:15:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/42047547.html
匿名

发表评论

匿名网友

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

确定