Getting started with Go language

Posted by jdog on Mon, 30 Mar 2020 12:36:39 +0200

goroutine and channel

goroutine

Multithreading

func hello() {
   //fmt.Printf("Hello Goroutine!!\n")
   for i:=0;i<100;i++ {
      fmt.Printf("hello:%d\n",i)
      time.Sleep(time.Millisecond)
   }
}

func main() {
   go hello()    //A separate thread has been started, alternating with the following code to make it a multithread
   //fmt.Printf("main function\n")
   for i:=0;i<100;i++ {
      fmt.Printf("main:%d\n",i)
      time.Sleep(time.Millisecond)
   }
   time.Sleep(time.Second)   //Fix the code so that the child thread can execute when the main thread exits
}

Multiple goroutine s

func nunmers() {
   for i :=0;i<=5;i++ {
      time.Sleep(time.Millisecond*250)
      fmt.Printf("%d\n",i)
   }
}

func chars() {
   for i:='a';i<='e';i++ {
      time.Sleep(time.Millisecond*400)
      fmt.Printf("%c\n",i)
   }
}

func main() {
   go nunmers()
   go chars()
   time.Sleep(time.Second*3)
}

Processes and threads

  • Process:

    • A process is an execution process of a program in the operating system. The system is an independent unit for resource allocation and scheduling
  • Threading:

    • Thread is an execution entity of a process and the basic unit of CPU scheduling and dispatching. It is smaller than a process and can run independently
  • A process can create and undo multiple threads, and multiple threads in the same process can execute concurrently

Concurrent and parallel

  • Multithreaded programs run on a core CPU, which is concurrent
  • Multithreaded programs run on multiple cores of CPU, which is parallel

Coroutines and threads

  • Collaboration: independent stack space, shared heap space, and scheduling controlled by users themselves. Essentially, it is similar to user level threads, which are also self implemented
  • Thread: a county can run multiple threads, which are lightweight threads

goroutine's scheduling model

  • M (thread) P (context) G (goroutine)

Set the number of CPU cores that golang runs

func main() {
   cpu := runtime.NumCPU()
   //Limit the number of cores (regardless of the new version), such as the monitoring program, which can control the resource consumption of its operation
   //runtime.GOMAXPROCS(1)
   for i:=0;i <=8;i++ {
      go func() {
      }()
   }
   fmt.Printf("%d\n",cpu)
   time.Sleep(time.Second*12)
}

channel

  • How to communicate between different goroutine s:

    • Global variables and lock synchronization: not recommended
    • Channel: first in first out queue
  • The concept of channel
    • Like a pipe in unix
    • FIFO
    • Thread safety, multiple goroutine colleagues do not need to lock access
    • There are types of channels. A certificate's channel can only be an integer

channel declaration

  • Reference type, need to use make to initialize
var Variable name chan type
var test chan int
var test chan string
var test chan map[string]string
var test chan stu
var test chan *stu
  • channel initialization, reading and writing
func main() {
   //Var int channel channel int = make (channel int, 1) channel with buffer
   var intChan chan int = make(chan int)      // Unbuffered channel
   fmt.Printf("intChanin:%p\n",intChan)
   go func() {
      //Write data
      intChan <- 100
      fmt.Printf("insert item end\n")
   }()

   go func() {
      //Read data
      fmt.Printf("start\n")
      time.Sleep(time.Second*3)
      var a int
      a = <- intChan
      fmt.Printf("intChan:%d\n",a)
   }()
   time.Sleep(time.Second*5)
}

Combination of goroutine and channel

  • Producer consumer model
func senData(ch chan string) {
   ch <- "Washinton"
   ch <- "Tripoli"
   ch <- "LongDong"
   ch <- "Beijing"
   ch <- "Tokyo"
}

func getData(ch chan string) {
   var input string
   for {
      input = <- ch
      fmt.Printf("input=%s\n",input)
   }
}

func main() {
   ch := make(chan string,10)
   go  senData(ch)
   go getData(ch)
   time.Sleep(time.Second*1)
}

Blocking channel

-No write, empty read will block (empty channel)
-If the number of writes exceeds the buffer, it will block (the channel is full)
func sendChans(ch chan string)  {
   var i int
   for {
      var str string
      str = fmt.Sprintf("stu %d\n",i)
      fmt.Printf("write:%s\n",str)
      //If there is no buffer, the write is blocked
      ch <- str
      i++
   }
}

func main() {
   ch := make(chan string,10)   //Buffer zone
   //Ch: = make (Chan string) / / no buffer
   go sendChans(ch)
   time.Sleep(time.Second*200)
}

Synchronization between channel s

  • Add exit detection method to ensure synchronization
func sendChannels(ch chan string,exitCh chan bool) {
   ch <- "AA"
   ch <- "BB"
   ch <- "CC"
   ch <- "DD"
   ch <- "EE"
   close(ch)
   exitCh <- true
}

func getChannels(ch chan string,exitCh chan bool) {
   for {
      // Check channel off
      input,ok := <- ch
      if !ok {
         break
      }
      fmt.Printf("getchannels Medium input:%s\n",input)
   }
   exitCh <- true
}

func getChannels2(ch chan string,exitCh chan bool) {
   for {
      // Check channel off
      input,ok := <- ch
      if !ok {
         break
      }
      fmt.Printf("getchannels Medium input:%s\n",input)
   }
   exitCh <- true
}

func main() {
   ch := make(chan string)
   exitChan := make(chan bool,3)
   go sendChannels(ch,exitChan)
   go getChannels(ch,exitChan)
   go getChannels2(ch,exitChan)
   //Check the status three times, and then exit. Do not need time.sleep, wait for other goroutine to exit
   <- exitChan    //Take out the elements and throw them away
   <- exitChan
   <- exitChan
}
  • Using waitGroup to ensure goroutine synchronization
func SendChannels1(ch chan string,waitGroup *sync.WaitGroup) {
   ch <- "AA"
   ch <- "BB"
   ch <- "CC"
   ch <- "DD"
   ch <- "EE"
   close(ch)
   fmt.Printf("send data exited\n")
   waitGroup.Done()    //End goroutine's flag, and then set the number - 1 of the flag bit
}

func GetChannels1(ch chan string,waitGroup *sync.WaitGroup) {
   for {
      input,ok := <- ch
      if !ok{
         break
      }
      fmt.Printf("getchannels Medium input:%s\n",input)
   }
   fmt.Printf("get data exited\n")
   waitGroup.Done()
}

func GetChannels2(ch chan string,waitGroup *sync.WaitGroup) {
   for {
      input,ok := <- ch
      if !ok{
         break
      }
      fmt.Printf("getchannels Medium input:%s\n",input)
   }
   fmt.Printf("get data2 exited\n")
   waitGroup.Done()
}

func main() {
   var wg sync.WaitGroup
   ch := make(chan string)
   wg.Add(3)  //Every time goroutine is executed, - 1
   go SendChannels1(ch,&wg)
   go GetChannels1(ch,&wg)
   go GetChannels2(ch,&wg)
   wg.Wait()   //Wait until the run is complete to return
   fmt.Printf("main goroutine exited\n")
}
  • When the channel is closed, the data of the producer channel is saved without loss
func main() {
   intChan := make(chan int,10)
   for i:=0;i<10;i++ {
      intChan<- i
   }
   //Close channel
   close(intChan)
   time.Sleep(time.Second)
   for j:=0;j<10;j++ {
      a := <- intChan
      fmt.Printf("a=%d\n",a)
   }
}
  • Traversal of channel s
func GetChannels2(ch chan string,waitGroup *sync.WaitGroup) {
   //for range will automatically determine whether the channel is closed
   for v :=range ch {
      fmt.Printf("get data2 %s\n",v)
   }

   fmt.Printf("get data2 exited\n")
   waitGroup.Done()
}
  • Closure of chan
    • Use the built-in function close to close. After the chan is closed, the for range traverses the existing elements in the chan
    • Use the built-in function close to close. After the chan is closed, there is no writing method for range. You need to use V, OK: = & lt; - chan to determine whether the chan is closed

Read only and write only of chan

It should be noted that & lt; - is in the position of chan keyword, and & lt; - is read-only on the left side of chan and write only on the right side

func chanPerms() {
   var readOnly <- chan int = make(chan int,100)
   // Readonly < - 100 read only and not writable
   var writeOnly chan <- int = make(chan int,10)
   // < - writeonly write only unreadable
}
  • Usage scenario: three parties call read-only write only permission control to prevent misoperation

select chan

func main() {
   var intChan chan int = make(chan int,10)
   var strChan chan string = make(chan string,10)
   var wg sync.WaitGroup
   wg.Add(2)
   // insert data
   go func() {
      var count int
      for count < 1000 {
         count++
         select {
         case intChan <- 10:
            fmt.Printf("write to int chan succ\n")
         case strChan <- "hello":
            fmt.Printf("write to str chan succ\n")
         default:
            fmt.Printf("all chan is full\n")
            time.Sleep(time.Second)
         }
      }
      wg.Done()
   }()
   wg.Wait()

   //Read data
   go func() {
      var count int
      for count < 10000 {
         count ++
         select {
         case a := <- intChan:
            fmt.Printf("read from int chain a:%d\n",a)
         case <- strChan:
            fmt.Printf("read from str chan\n")
         default:
            fmt.Printf("all chan is empty\n")
            time.Sleep(time.Second)
         }
      }
      wg.Done()
   }()
   wg.Wait()
}

Topics: Go Unix