The opening question
- Know the concepts of concurrency, parallelism, threading, coroutines? Or do you know what it means?
- Why do we have coroutines when we have threads? What’s the difference?
- How many ways do processes communicate? Are there more than three?
- Golang “coroutine” communication method recommended?
- What is the purpose of using concurrency? Is it gonna help us solve something?
concept
Concurrency, threads, coroutines: concepts are impossible to conceptualize, Google them. Or click here to check out the previous article
- Parallelism: Usually refers to multiple CPU instances or multiple machines executing a logic (method) at the same time
Process communication mode
The name of the | The characteristics of |
---|---|
Pipes/Anonymous Pipes (pipe) | The essence of a pipe is a kernel buffer |
Named pipe (FIFO) | First in first out; Exists in a file system as a named pipe; |
Signal (Signal) | You do not need to know the state of the process; Blocking process; Asynchronous communication; |
Message queue (Message Queue) | A list of linked messages placed in the kernel; Allows one or more processes to write and read messages to it; It overcomes the defect that the signal carries less information; There are two main types of message queues: POSIX message queues and System V message queues. System V message queues are widely used. |
Shared memory (share memory) | Enables multiple processes to directly read and write the same memory space, is the fastest form of IPC available; Since multiple processes share a memory, some synchronization mechanism (such as semaphore) is needed to achieve synchronization and mutual exclusion between processes. |
Semaphores (semaphore) | A semaphore is a counter used by multiple processes to access shared data. The intent of a semaphore is that interprocess synchronization can only be performed by two standard atoms: WAIT (SEMAP) and signal(SEMAP); For a visit Semaphores are non-negative integer variables Operations are also known as PV primitives (P from Dutch proberen” to test “, V from Dutch verhogen” to increase “, P meaning to pass, V meaning to release) |
Socket (socket) | A socket is the basic unit of operation for network communication that supports TCP/IP The characteristics of a socket are determined by three attributes: domain, port number, and protocol type. |
The differences between semaphores and mutex are as follows: (1) The mutex is used for the mutex of the thread, while the semaphores are used for the synchronization of the thread. This is the fundamental difference between mutex and semaphore, the difference between mutex and synchronization. ** mutually exclusive: ** is unique and exclusive, allowing only one visitor to access a resource at a time. However, mutual exclusion cannot limit the order in which visitors access resources, that is, the access is out of order. ** Synchronization: ** refers to orderly access to resources by visitors through other mechanisms on a mutually exclusive basis (in most cases). In most cases, synchronization already implements mutual exclusion, and in particular all written resources must be mutually exclusive. In rare cases, multiple visitors can access the resource simultaneously. (2) The mutex value can only be 0/1, and the signal value can be a non-negative integer. That is, a mutex can only be used for exclusive access to one resource, and it cannot implement the problem of multi-threaded mutex for multiple resources. Semaphores can achieve multi-threaded mutual exclusion and synchronization of multiple resources of the same type. When a semaphore is a single-valued semaphore, it is also possible to perform mutually exclusive access to a resource. (3) Mutex lock and unlock must be used by the same thread, the semaphore can be released by one thread, another thread to obtain.
Go coroutine communication is used
Basically, channel is recommended, this is the most recommended form of use;
There is also the use of sync.Mutex Mutex lock communication;
More detailed introduction will be written later;
Go uses coroutines in some application scenarios, simple examples
- To perform discrete “loops”, you need to wait for the results to be calculated
In this case, it is generally different “data sets” that need to be “processed”. In the process of processing, the influence of two data sets on “results” does not have sequence lines;
In this case, it is perfectly possible to use two data for coroutine processing separately and then perform subsequent operations;
/ / pseudo code
var result, data1, data2 int32
done1 := make(chan bool)
done2 := make(chan bool)
// The first set of data needs to be summed up
go func(a) {
for _, val := range dataset1 {
data1 += val
}
done1 <- true
}
// The second set of data needs to be summed
go func(a) {
for _, val := range dataset2 {
data2 += val
}
done2 <- true
}
// Wait for the coroutine to complete the operation
<-done1
<-done2
// Add the results
result = data1 + data2
Copy the code
- Need to enter other irrelevant business, do not delay the “main coroutine” return value, do not wait
Generally, after some business is processed, some “extra work” needs to be done without delaying the return of the main coroutine, and then a coroutine can be opened to do it without waiting
/ / pseudo code
result, err := processMethod()
iferr ! =nil{... }// Other operations such as write caching are required to ensure that data is not returned
go func(a) {
err = saveRedis(result)
iferr ! =nil{... }}return result
Copy the code
- Set time limits for certain tasks and “timeout close” the current operation
For example, if some data is sent through a channel, the sending is automatically aborted and the channel is closed.
// Define two buffered channels with capacity 1 respectively
c1 := make(chan string.1)
c2 := make(chan string.1)
go func(a) {
time.Sleep(time.Second * 1) // Send data every 1 second
c1 <- "data1"} ()go func(a) {
time.Sleep(time.Second * 6) // Send data every 6 seconds
c2 <- "data2"} ()for i := 0; i < 2; i++ {
// Create a tolerance time for the channel. If you cannot read or write within 5 seconds, return immediately
tm := time.NewTimer(time.Second * 5)
// Use select to get the values of the two channels, and then print
select {
case data1 := <-c1: // Receive c1 channel data (consume data)
fmt.Println(data1)
case data2 := <-c2: // Receive C2 channel data (consume data)
fmt.Println(data2)
case <-tm.C:
fmt.Println("timeout!")}}Copy the code
At the end of the essay questions
- Have you used coroutines? Know the difference between a coroutine and a thread?
- If you have a single-core CPU, is it useful to open coroutines?
- Does this article help you improve your code ability?
- Do you know anything about process communication?
- Would you use the coroutine examples in this article to speed things up?