英文:
Assembly Line in Golang using concurrency
问题
新手学习Go语言。我正在尝试编写一个“装配线”,其中多个函数像工人一样相互传递某个数据结构,每个函数对数据结构进行一些操作。
type orderStruct struct {
orderNum, capacity int
orderCode uint64
box [9]int
}
func position0(in chan orderStruct) {
order := <-in
if ((order.orderCode << 63) >> 63 == 1) {
order.box[order.capacity] = 1
order.capacity += 1
}
fmt.Println(" 在位置0填充盒子 {", order.orderNum, order.orderCode, order.box, order.capacity, "} ")
}
func startOrder(in chan orderStruct) {
order := <-in
fmt.Printf("\n为客户订单号为%d,请求号为%d的订单开始一个空盒子\n", order.orderNum, order.orderCode)
fmt.Println(" 开始盒子 {", order.orderNum, order.orderCode, order.box, order.capacity, "}")
d := make(chan orderStruct, 1)
go position0(d)
d <- order
}
func main() {
var orders [10]orderStruct
numOrders := len(os.Args) - 1
var x int
for i := 0; i < numOrders; i++ {
x, _ = strconv.Atoi(os.Args[i+1])
orders[i].orderCode = uint64(x)
orders[i].orderNum = i + 1
orders[i].capacity = 0
for j := 0; j < 9; j++ {
orders[i].box[j] = 0
}
c := make(chan orderStruct)
go startOrder(c)
c <- orders[i]
}
}
所以,我遇到的问题是,在startOrder()
函数中的打印语句可以正常执行,但是当我尝试将结构体传递给position0()
函数时,没有任何内容被打印出来。我是否对通道的工作原理有误解?
英文:
New to Go. I'm attempting to code an "assembly line" where multiple functions act like workers and pass some data structure to each other down the line, each doing something to the data structure.
type orderStruct struct {
orderNum,capacity int
orderCode uint64
box [9]int
}
func position0(in chan orderStruct){
order := <-in
if((order.orderCode<<63)>>63 == 1){
order.box[order.capacity] = 1
order.capacity += 1
}
fmt.Println(" filling box {", order.orderNum, order.orderCode, order.box, order.capacity, "} at position 0")
}
func startOrder(in chan orderStruct){
order := <-in
fmt.Printf("\nStart an empty box for customer order number %d , request number %d\n", order.orderNum, order.orderCode)
fmt.Println(" starting box {", order.orderNum, order.orderCode, order.box, order.capacity, "}")
d := make(chan orderStruct,1)
go position0(d)
d <- order
}
func main() {
var orders [10]orderStruct
numOrders := len(os.Args)-1
var x int
for i := 0; i < numOrders; i++{
x, _ = strconv.Atoi(os.Args[i+1])
orders[i].orderCode = uint64(x)
orders[i].orderNum = i+1
orders[i].capacity = 0
for j := 0; j < 9; j++{
orders[i].box[j] = 0
}
c := make(chan orderStruct)
go startOrder(c)
c <- orders[i]
}
}
So basically the issue I'm having is that the print statements in startOrder() execute fine, but when I try to pass the struct to position0(), nothing is printed. Am I misunderstanding how channels work?
答案1
得分: 1
我已经尝试重新编写了你的代码,以使其正常工作。你可以在playground上运行它。
主要的区别是:
- 只启动了两个goroutine,它们充当生产线上的两个工人,一个接受订单,另一个装箱。
- 使用sync.WaitGroup来确定它们何时结束。
- 使用
for x := range channel
。 - 使用
close(c)
来表示通道的结束。 - 你可以启动多个每个工人的副本,代码仍然可以正常工作(重复
wg.Add(1); go startOrders(c, wg)
的代码)。
以下是代码:
package main
import (
"fmt"
"sync"
)
type orderStruct struct {
orderNum, capacity int
orderCode uint64
box [9]int
}
func position0s(in chan orderStruct, wg *sync.WaitGroup) {
defer wg.Done()
for order := range in {
if (order.orderCode<<63)>>63 == 1 {
order.box[order.capacity] = 1
order.capacity += 1
}
fmt.Println(" 在位置0填充箱子 {", order.orderNum, order.orderCode, order.box, order.capacity, "} ")
}
}
func startOrders(in chan orderStruct, wg *sync.WaitGroup) {
defer wg.Done()
d := make(chan orderStruct)
wg.Add(1)
go position0s(d, wg)
for order := range in {
fmt.Printf("\n为客户订单号 %d ,请求号 %d 开始一个空箱子\n", order.orderNum, order.orderCode)
fmt.Println(" 开始箱子 {", order.orderNum, order.orderCode, order.box, order.capacity, "}")
d <- order
}
close(d)
}
func main() {
var orders [10]orderStruct
numOrders := 4
var x int = 10
wg := new(sync.WaitGroup)
c := make(chan orderStruct)
wg.Add(1)
go startOrders(c, wg)
for i := 0; i < numOrders; i++ {
orders[i].orderCode = uint64(x)
orders[i].orderNum = i + 1
orders[i].capacity = 0
for j := 0; j < 9; j++ {
orders[i].box[j] = 0
}
c <- orders[i]
}
close(c)
wg.Wait()
}
英文:
I've attempted to re-write what you've written to work properly. You can run it on the playground
The main differences are
- only two go routines are started - these act as the two workers on the production line - one taking orders and the other filling boxes
- use of sync.WaitGroup to find out when they end
- use of for x := range channel
- use of close(c) to signal end of channel
- you could start multiple copies of each worker and the code would still work fine (repeat the
wg.Add(1); go startOrders(c, wg)
code)
Here is the code
package main
import (
"fmt"
"sync"
)
type orderStruct struct {
orderNum, capacity int
orderCode uint64
box [9]int
}
func position0s(in chan orderStruct, wg *sync.WaitGroup) {
defer wg.Done()
for order := range in {
if (order.orderCode<<63)>>63 == 1 {
order.box[order.capacity] = 1
order.capacity += 1
}
fmt.Println(" filling box {", order.orderNum, order.orderCode, order.box, order.capacity, "} at position 0")
}
}
func startOrders(in chan orderStruct, wg *sync.WaitGroup) {
defer wg.Done()
d := make(chan orderStruct)
wg.Add(1)
go position0s(d, wg)
for order := range in {
fmt.Printf("\nStart an empty box for customer order number %d , request number %d\n", order.orderNum, order.orderCode)
fmt.Println(" starting box {", order.orderNum, order.orderCode, order.box, order.capacity, "}")
d <- order
}
close(d)
}
func main() {
var orders [10]orderStruct
numOrders := 4
var x int = 10
wg := new(sync.WaitGroup)
c := make(chan orderStruct)
wg.Add(1)
go startOrders(c, wg)
for i := 0; i < numOrders; i++ {
orders[i].orderCode = uint64(x)
orders[i].orderNum = i + 1
orders[i].capacity = 0
for j := 0; j < 9; j++ {
orders[i].box[j] = 0
}
c <- orders[i]
}
close(c)
wg.Wait()
}
答案2
得分: 1
管道是学习在Go中并发编程的好起点。Nick Craig-Wood的答案提供了解决这个具体挑战的工作解决方案。
在Go中,还有许多其他使用并发的方式。大体上,可以根据并发处理的对象将其分为三类:
-
功能分解 - 创建多个函数的管道是一个很好的入门方式,也是你问题的主题。这种方式很容易理解,而且很高效。然而,如果进展到真正的并行硬件,很难很好地平衡负载。所有的操作都以最慢的管道阶段的速度进行。
-
几何分解 - 将数据分割成可以独立处理(或者通信较少)的区域。基于网格的系统在某些科学高性能计算领域中很受欢迎,比如天气预报。
-
任务分配 - 确定要完成的工作如何被分割成(大量的)任务,并逐个将这些任务分配给“工作者”,直到所有任务都完成。通常,任务的数量远远超过工作者的数量。这个类别包括所有所谓的“尴尬并行”问题(尴尬的原因是如果你的高性能系统无法实现线性加速,你看起来有点傻)。
我可以添加第四类,即上述几种方式的混合方式。
关于这个问题有很多文献,包括80年代和90年代Occam编程的许多内容。Go和Occam都使用CSP消息传递,所以问题是相似的。我推荐一本有帮助的书籍《Practical Parallel Processing: An introduction to problem solving in parallel》(Chalmers and Tidmus 1996)。
英文:
Pipelines are a great place to start when learning to program concurrently in Go. Nick Craig-Wood's answer provides a working solution to this specific challenge.
There is a whole range of other ways to use concurrency in Go. Broadly, there are three categories divided according to what is being treated as concurrent:
-
Functional decomposition - Creating pipelines of several functions is a good way to get started - and is your question's topic. It's quite easy to think about and quite productive. However, if it progresses to truly parallel hardware, it's quite hard to balance the load well. Everything goes at the speed of the slowest pipeline stage.
-
Geometric decomposition - Dividing the data up into separate regions that can be processed independently (or without too much communication). Grid-based systems are popularly used in certain domains of scientific high-performance computing, such as weather-forecasting.
-
Farming - Identifying how the work to be done can be chopped into (a large number of) tasks and these tasks can be allocated to 'workers' one by one until all are completed. Often, the number of tasks far exceeds the number of workers. This category includes all the so-called 'embarrassingly parallel' problems (embarrassing because if you fail to get your high-performance system to give linear speed-up, you look a bit daft).
I could add a fourth category of hybrids of several of the above.
There is quite a lot of literature about this, including much from the days of Occam programming in the '80s and '90s. Go and Occam both use CSP message passing so the issues are similar. I would single out the helpful book Practical Parallel Processing: An introduction to problem solving in parallel (Chalmers and Tidmus 1996).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论