danger
The inter-process communication feature provided by the gproc
component is experimental!
Do not communicate by sharing memory; instead, share memory by communicating.
There are 5
common methods of inter-process communication: pipe/signal/shared memory/shared file/Socket
, each typically having its preferred usage scenarios.
Signal
: Signals are commonly used in*nix
systems, with poor cross-platform capabilities and simple methods and contents for information transmission.Pipe
: Including ordinary pipes and named pipes, this method is commonly used in parent-child process communication scenarios and is not very suitable for communication between unrelated processes.Shared Memory/Shared File
: In terms of concurrent architecture design, we try to minimize the use oflock mechanisms
, including shared memory (memory locks)/shared files (file locks), which actually require lock mechanisms to ensure the correctness of data flow. The maintenance complexity often outweighs the benefits brought by the lock mechanisms.
The primary mechanism that gproc
implements for inter-process communication is Socket
, which has the advantage of functionality stability and general usage scenarios.
The API for inter-process communication in gproc
is extremely simple, achieved through the following two methods:
func Send(pid int, data []byte) error
func Receive() *Msg
We use the Send
method to send data to a specified process (each call is equivalent to sending a message), and in the specified process, we can obtain the data through the Receive
method. The Receive
method offers a message queue-like approach to receive data from other processes. It will block
and wait when the queue is empty.
Let’s look at a basic usage example of inter-process communication:
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))
}
}
}
In this example, our main process creates a child process upon startup. The child process sends the current time to the main process every second, and the main process outputs the received parameters from the child process to the terminal. After execution, the content output in the terminal is as follows:
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
...