目前gproc
组件提供的进程通信特性属于实验性特性!
不要通过共享内存来通信,而应该通过通信来共享内存。
常见的进程通信方式有5
种:管道/信号/共享内存/共享文件/Socket
,这几种方式通常有着各自比较擅长的使用场景。
信号
:信号常用在*nix
系统中,跨平台性比较差,信息传递方式和内容单一。管道
:包括普通管道与命名管道,这种方式在子父进程通信场景比较常用,跨不同关系进程之间通信不太适用。共享内存/共享文件
:按照并发架构的设计来讲,我们尽可能地少用锁机制
,包括共享内存(内存锁)/共享文件(文件锁)其实都是需要依靠锁机制才能保证数据流的正确性,因为锁机制带来的维护复杂度往往会比其带来的收益更高。
gproc
实现的进程通信主要机制采用的是Socket
,这种机制优点是功能比较稳定、使用场景比较通用。
gproc
的进程通信API非常简便,只需通过以下两个方法实现:
func Send(pid int, data []byte) error func Receive() *Msg
我们通过Send
方法向指定的进程发送数据(每调用一次相当于发送一条消息),在指定的进程中可以通过Receive
方法获得数据。其中,Receive
方法提供了类似消息队列的形式来收取其他进程传递的数据,当队列为空时,该方法将会阻塞
等待。
我们来看一个进程间通信的基本使用示例:
package main import ( "context" "fmt" "github.com/gogf/gf/v2/os/gctx" "github.com/gogf/gf/v2/os/gproc" "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/os/gtimer" "os" "time" ) var ( ctx = gctx.New() ) func main() { fmt.Printf("%d: I am child? %v\n", gproc.Pid(), gproc.IsChild()) if gproc.IsChild() { gtimer.SetInterval(ctx, time.Second, func(ctx context.Context) { err := gproc.Send(gproc.PPid(), []byte(gtime.Datetime())) if err != nil { return } }) select {} } else { m := gproc.NewManager() p := m.NewProcess(os.Args[0], os.Args, os.Environ()) p.Start(ctx) for { msg := gproc.Receive() fmt.Printf("receive from %d, data: %s\n", msg.SenderPid, string(msg.Data)) } } }
该示例中,我们的主进程启动时创建了一个子进程,该子进程每隔1秒钟向主进程发送当前的时间,主进程收取到子进程发送的参数后输出到终端上。执行后,终端输出的内容如下:
29978: I am child? false 29984: I am child? true receive from 29984, data: 2018-05-18 15:01:00 receive from 29984, data: 2018-05-18 15:01:01 receive from 29984, data: 2018-05-18 15:01:02 receive from 29984, data: 2018-05-18 15:01:03 receive from 29984, data: 2018-05-18 15:01:04 ...