英文:
Why is there no `.join` operation on goroutines?
问题
为什么在goroutine上没有.join
操作(即阻塞直到完成)的操作?而是需要使用通道来在主goroutine中进行通信以确定何时继续进行?
感觉我对CSP理论中的某些基本知识有所遗漏。
编辑:
我问的是“为什么是这样”,而不是“如何实现这个”。
英文:
Why is there no .join
operation (i.e., block until finished) operation on goroutines? Instead of having to use channels to communicate when to proceed in the main goroutine?
It feels like I'm missing something fundamental about the theory around CSP.
EDIT:
I'm asking "why is it so", not "how do I accomplish this".
答案1
得分: 4
这是因为goroutine不是线程。一个给定的goroutine可以由Go运行时调度,与操作系统线程关联,但是例如在阻塞I/O操作的情况下,该线程可以与其他goroutine关联,而另一个goroutine正在等待。
这意味着什么?
加入(joining)需要一个同步对象来知道何时线程完成。由于Go的goroutine实际上只是具有堆栈的非常轻量级的对象,它们不直接提供这样的同步对象。
Go的CSP原则是,你可以非常廉价地实例化成千上万个goroutine,并且只使用与物理CPU核心数量相同的线程。
从操作系统的角度来看,同步对象是昂贵的,因此为每个goroutine都提供这样的对象将非常低效。
相反,通过使用通道或sync包中的WaitGroup来实现同步。
英文:
This is simply because goroutines are not threads. A given goroutine can be scheduled by the Go runtime to be associated to an Operating System thread, but for example in the case of blocking I/O operations, said threads can be associated to other goroutines while the other one is waiting.
What does this mean?
Joining needs a synchronization object in order to know when a thread is finished. As Go's goroutines are actually just very lightweight objects that only possess a stack, they do not provide such synchronization objects directly.
Go's CSP premise is that you can instanciate thousands of goroutines very cheaply, and only use as many threads as you have physical CPU cores.
In the perspective of the OS, synchronization objects are expensive, therefore having such objects for each goroutine would be very inefficient.
Instead, synchronization is attained by using channels or WaitGroup's from the sync package.
答案2
得分: 0
以下是翻译的内容:
在@SirDius的详细解释的基础上,我给出一个使用sync.Mutex
的简单示例。我选择了sync.Mutex
而不是sync.WaitGroup
,因为我只打算让每个Thread
对象包装一个单独的goroutine,而WaitGroups是用于多个goroutine的。
type Thread struct {
body func()
mux sync.Mutex
}
func NewThread(body func ()) *Thread {
return &Thread{
body: body,
}
}
func (thread *Thread) Start() {
thread.mux.Lock()
go thread.run()
}
func (thread *Thread) Join() {
thread.mux.Lock()
thread.mux.Unlock()
}
func (thread *Thread) run() {
thread.body()
thread.mux.Unlock()
}
你可以按照以下方式使用它:
func main() {
t := NewThread(t1)
t.Start()
t.Join()
print("Thread 1: Joined\n")
}
func t1() {
print("Thread 1: Started\n")
time.Sleep(5 * time.Second)
print("Thread 1: Completed\n")
}
英文:
Following up on @SirDius' detailed explanation
Here is a simple example demonstrating how to do this with a sync.Mutex
. I chose a sync.Mutex
over a sync.WaitGroup
here since I only intended for each Thread
object to wrap a single goroutine, and WaitGroups are intended to be used with multiple goroutines.
type Thread struct {
body func()
mux sync.Mutex
}
func NewThread(body func ()) *Thread {
return &Thread{
body: body,
}
}
func (thread *Thread) Start() {
thread.mux.Lock()
go thread.run()
}
func (thread *Thread) Join() {
thread.mux.Lock()
thread.mux.Unlock()
}
func (thread *Thread) run() {
thread.body()
thread.mux.Unlock()
}
You could use it as follows
func main() {
t := NewThread(t1)
t.Start()
t.Join()
print("Thread 1: Joined\n")
}
func t1() {
print("Thread 1: Started\n")
time.Sleep(5 * time.Second)
print("Thread 1: Completed\n")
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论