Skip to main content
Version: 2.8.x(Latest)
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 of lock 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
...