The concept of concurrency and its importance

This is simple science. You can skip it

Concurrency: A concurrent program is a program that performs multiple tasks simultaneously. In the operating system, it refers to a period of time between the start and finish of several programs, and these programs are running on the same processor, but at any point in time only one program is running on the processor.

———- This quote is from Advanced Programming with the GO language

In the early days, cpus sequentially executed machine instructions in the form of single cores. C, the ancestor of Go, is the representative of this sequential programming language. Sequence in sequential programming languages means that all instructions are executed sequentially, with one and only one CPU executing the program’s instructions sequentially at the same time.

With the development of processor technology, in the single-core era, the way of increasing processor frequency to improve operation efficiency has met a bottleneck. At present, various mainstream CPU frequency is basically locked around 3GHZ. The stagnation of the development of single-core CPU brings opportunities to the development of multi-core CPU. Accordingly, programming languages have begun to move towards parallelization. Go language is a native concurrency programming language born under the background of multi-core and network era.

Before we talk about concurrency, let’s talk about shared variables, threads, and coroutines

  1. How to share variables/memory between different threads/coroutines?

Here leave everybody to see an officer to check data by oneself, although I list to also be inferior to oneself start work to check memory profound!

You can wait for my next article to explain threads and processes in more detail.

  1. Thread and coroutine concepts?

** threads: ** threads are the smallest unit of operation scheduling that an operating system can do. A process can contain multiple threads and is the actual operating unit of the process.

** coroutines: ** also known as microthreads. Coroutines are lightweight user – mode threads. Coroutines have their own register context and stack. When coroutine schedules a switch, the register context and stack are saved elsewhere, and when cut back, the previously saved register context and stack are restored.

  1. Why was coroutines born?

Although multithreading has been used enough in the pre-Internet generation, the limitations of threads are also obvious

  1. There are a limited number of threads, usually not many
  2. Threads often take up far more resources than we need, resulting in waste

Each system-level thread startup takes up space, which may be at the Megabyte level, but if we use threads that only need to pass kilobytes of data, then threads seem wasteful but unavoidable. There is also some overhead associated with switching between threads.

To solve the above contradiction, coroutines were born: lower cost of resources, dynamic allocation of resources, and lighter than threads.

Some advantages of coroutines:

  1. Because subroutine switching is not thread switching but controlled by the program itself, there is no overhead of thread switching, and the more threads there are, the greater the performance advantage of coroutines compared to multithreading.
  2. Do not need multithreaded locking mechanism, because there is only one thread, there is no conflict of variables written at the same time, in the coroutine control of shared resources do not lock, only need to judge the state, so the execution efficiency is much higher than multithreading.

In Golang, the cost of creating a Goroutine is very small, at the KB level. As a result, more coroutines can be created, especially if there are more coroutines than threads, and Goroutines can scale dynamically with less stack overflow risk than threads.

Golang concurrency, goroutine use

var name = "yiyiyinhe"

func changeName(a) {
    name = "change"
}

func sayHi(a) {
    fmt.println("hi, ", name)
    go changeName() / / coroutines
}
Copy the code

A simple coroutine is created, so it could print out hi yiyiyinhe or hi change.

If you want to execute a coroutine on a block of code instead of a method, use the following

var name = "yiyiyinhe"

func sayHi(a) {
    fmt.println("hi, ", name)
    go func(a) { // Anonymous functions execute coroutines
        name = "change"}}Copy the code

channel

Golang specifies the number of shared variables

Do not communicate by sharing memory; instead, share memory by communicating.

Do not communicate by sharing memory, but share memory by communicating.

Communication is also required in coroutines, and the main method of communication and synchronization between Goroutines used by Golang is channel.

What is a channel?

A channel is a communic ation mechanism that lets one goroutine send values to another goroutine. Each channel is a conduit for values of a particular type, called the channel’s element type.

A channel is a communication mechanism that lets one Goroutine send values to another goroutine. Each channel is a pipeline of values of a specific type (channel element type).

To put it simply, a channel is a channel used to communicate in a Goroutine, and there are specific types of channels.

A channel can be created with or without cache. The difference is whether the size is allocated during channel creation

  • No cache channel

    • var ch1 = make(chan int), unallocated size
    • var ch2 = make(chan int, 0), an allocated size of 0 is also equal to an unallocated size
  • Have a cache channel

    • Var: ch3 = make(int, 3), allocate size 3 has cache channel

In an uncached channel, the send operation of a channel always occurs before the receive. A caches channel is a channel that must be sent from the beginning of flag<-true to the tail <-flag, and the tail must occur after the header is sent.

  • Why can Chennel do that?

    Because a channel blocks, it must be received before it can continue.

A cached channel does not have this feature, because for a buffered channel, the KTH receive completion for a channel occurs before the K+C send completion, where C is the cache size of the channel. If C is set to 0, it naturally corresponds to a Channel with no cache, even if the KTH receive is completed before the KTH send is completed. Since only one unbuffered Channel can be sent synchronously, this simplifies to the previous rule of unbuffered channels: receiving from an unbuffered Channel occurs before sending to that Channel is complete.

Again, use channel to illustrate the above example

var name = "yiyi"
var flag = make(chan bool) // create a channel of type bool

func changeName(a) {
    name = "change"
    flag <- true  / / send
}

func sayHi(a) {
    go changeName() / / coroutines
    <-flag / / receive
    fmt.println("hi, ", name)
}
Copy the code

In this case, a fixed order is printed. Since <-flag is always received after sending, name = “change” is executed before flag < -true is executed, and the printed result must be hi, change

The above code is equivalent to what is shown below

What can you get out of it?

  1. Learn about concurrency and the history of multithreading
  2. Simple understanding of threads, coroutines
  3. Why were coroutines born?
  4. Two ways to use goroutine
  5. What is a channel? Two categories of channel;
  6. What does a channel do in Goroutine?

Write in the last

Because I just started writing technical articles, a lot of things I don’t know how to write to let everyone understand, just like writing the thread coroutines don’t know what do you want to explain what is Shared variables or Shared memory, also don’t know if you can know what way multithreaded mode communication between threads, feel want to write and think you see the article title should be understand something, If you write too long, do not write and do not understand; Feel more contradictory bar, also hope everybody can give me a few suggestions!

The author is still working hard, trying to make the article easy to understand, accurate typesetting, grasp the key points, and show the best content to everyone.