1. What is a goroutine? How do you stop it?#
- A goroutine is a function or method that executes concurrently with other goroutines in a special goroutine thread. Goroutine threads are lighter weight than standard threads, and most Golang programs use thousands of goroutines simultaneously.
- To create a goroutine, add the keyword "go" before the function declaration.
- You can stop a goroutine by sending a signal through a channel. Goroutines only respond to signals when they are instructed to check, so you need to include the check at a logical position (e.g., at the top of a for loop).
2. What are the characteristics of a synchronization lock? What is its purpose?#
- In Go, communication is used for memory sharing instead of shared memory. Go's Communicating Sequential Process (CSP) concurrency model is implemented using goroutines and channels.
- When a goroutine acquires a mutex, other goroutines have to wait until the mutex is released. RWMutex allows multiple readers to hold the lock or only one writer to hold the lock. When the lock is held by a writer, it blocks any other goroutine (both readers and writers) from entering. The purpose of a synchronization lock is to ensure exclusive access to resources, preventing data corruption due to concurrency and ensuring system stability.
3. What are the characteristics of a channel? What should be noted?#
- Sending data to a nil channel will cause it to block indefinitely.
- Receiving data from a nil channel will also cause it to block indefinitely.
- Sending data to a closed channel will cause a panic.
- Receiving data from a closed channel will return a zero value if the buffer is empty.
4. What is GoConvey? What is it commonly used for?#
- GoConvey is a unit testing framework for Golang.
- GoConvey can automatically monitor file modifications and start tests, and it can output test results in real-time to a web interface.
- GoConvey provides rich assertions to simplify the writing of test cases.
5. What is the difference between make and new in Go?#
- Both make and new are used for memory allocation.
- make is used for initializing slices, maps, and channels, and it returns the initialized reference type itself.
- new is used for allocating memory for a type and initializes the memory with the zero value of the type. It returns a pointer to the type.
6. What is the difference between an array and a slice in Go?#
- Array:
- Arrays have a fixed length. The length is part of the array type, so [3]int and [4]int are two different array types. Arrays need to specify the size, but if not specified, the size can be automatically inferred based on the initialization. The size cannot be changed. Arrays are passed by value.
- Slice:
- Slices can change their length. Slices are lightweight data structures with three properties: pointer, length, and capacity. Slices do not need to specify a size. Slices are passed by reference (or called pass by value of the underlying array). Slices can be initialized using an array or using the built-in function make(), which initializes the length and capacity to the same value and can be expanded.
7. What is the purpose and characteristics of defer?#
- The purpose of defer is to delay the execution of a function until the surrounding function returns. When a defer statement is executed, the function following the defer keyword is deferred and will be executed after the surrounding function completes, regardless of whether the function returns normally or panics. Multiple defer statements can be used in a function, and they are executed in reverse order of declaration.
- Common use cases for defer include handling paired operations such as opening and closing, connecting and disconnecting, locking and unlocking.
- With defer, resources can be released in any execution path, ensuring resource cleanup even in the presence of errors.
- Defer statements that release resources should be placed immediately after the statements that acquire the resources.
8. How to use WaitGroup?#
A WaitGroup object can be used to wait for a group of goroutines to finish. The usage is as follows:
- The main goroutine calls wg.Add(delta int) to set the number of worker goroutines and then creates the worker goroutines.
- After the worker goroutines finish their execution, they call wg.Done().
- The main goroutine calls wg.Wait() and blocks until all worker goroutines have finished executing.
9. How does WaitGroup work?#
- WaitGroup maintains two counters: a request counter (v) and a wait counter (w). They form a 64-bit value, with the request counter occupying the higher 32 bits and the wait counter occupying the lower 32 bits.
- Each time Add is called, the request counter (v) is incremented. Each time Done is called, the wait counter (w) is decremented. When v becomes 0, Wait() is unblocked.
10. What is sync.Once?#
- sync.Once is used to perform an action only once. It is commonly used for initializing singleton objects.
- With sync.Once, you can call the Do method multiple times, but the action (specified by the function parameter) will only be executed once, on the first call to Do. The function parameter should be a function with no parameters and no return value.
11. What is an atomic operation?#
- An atomic operation is an operation that cannot be interrupted during its execution. During the execution of an atomic operation, the CPU will not perform any other operations on the same value. Atomic operations are represented and executed by a single CPU instruction. They are lock-free and often directly implemented using CPU instructions. Other synchronization techniques often rely on atomic operations for their implementation.
12. What is the difference between atomic operations and locks?#
- Atomic operations are supported by the underlying hardware, while locks are implemented by the operating system's scheduler. Locks are used to protect a section of code and ensure exclusive access to a variable for updates.
- Atomic operations are generally more efficient and can take advantage of the multi-core capabilities of a computer. If the update involves a complex object, it is recommended to use the atomic.Value type for encapsulation.
13. What is a microservice?#
- A microservice, also known as a microservices architecture, is an architectural style that structures an application as a collection of small, autonomous services based on business domains.
In simple terms, think of how bees build their honeycomb by aligning hexagonal wax cells. They start with small parts made of various materials and continue building a larger honeycomb from them. The cells form patterns and create a strong structure that holds specific parts of the honeycomb together.
Here, each cell is independent of another cell, but it is also related to other cells. This means that damage to one cell does not affect other cells, so bees can rebuild these cells without affecting the integrity of the entire honeycomb.