Resumen de preguntas y respuestas seleccionadas de la entrevista de alta frecuencia de Golang

Hola a todos, soy el hermano Yang.

El "Resumen de las preguntas de la entrevista GO Must Know and Meet" escrito anteriormente se ha leído más de 10 000 veces y tiene más de 230 favoritos.

También puede recopilar y reenviar este artículo.

Este artículo clasifica 17 preguntas de entrevistas de alta frecuencia del idioma Go y respuestas detalladas para usted Cada pregunta se proporciona 代码示例para facilitar su mejor comprensión.

1. Seguridad de concurrencia

¿Qué es la seguridad de concurrencia en el lenguaje Go? ¿Cómo garantizar la seguridad de la concurrencia?

respuesta:

La seguridad de la concurrencia significa que en la programación concurrente, el acceso a los recursos compartidos por parte de varias rutinas no generará competencia de datos ni resultados inciertos.

Para garantizar la seguridad de la concurrencia, se pueden tomar las siguientes medidas:

  • Use mutexes (Mutex): al usar mutexes para proteger el acceso a los recursos compartidos, solo una gorutina puede acceder a los recursos compartidos a la vez, evitando así las condiciones de carrera.
  • Use operaciones atómicas (Operaciones atómicas): para operaciones simples de lectura y escritura, puede usar operaciones atómicas para garantizar la atomicidad de las operaciones y evitar condiciones de carrera.
  • Usar canales (Channel): Al usar canales de comunicación y sincronización entre rutinas, evita el acceso directo a los recursos compartidos.
  • Use el mecanismo de sincronización: use el mecanismo de sincronización, como el grupo de espera (WaitGroup), la variable de condición (Cond), etc. para coordinar el orden de ejecución y el estado de varias rutinas.

A través de las medidas anteriores, se puede garantizar la seguridad de los programas concurrentes y se pueden evitar carreras de datos y resultados indeterminados.

2. aplazar

¿Cuál es la función de la palabra clave diferir en el lenguaje Go? Dé un ejemplo del uso de defer.

respuesta:

La palabra clave defer se usa para retrasar la ejecución de una función, es decir, para realizar una operación antes de que la función finalice. Defer se usa generalmente para liberar recursos, cerrar archivos, desbloquear mutexes y otras operaciones de limpieza para garantizar que se procesen después de ejecutar la función.

También puede usar la instrucción defer junto con el paquete de tiempo para implementar estadísticas sobre el tiempo de ejecución de la función.

Ejemplo de código:

Aquí hay un ejemplo que usa diferir para abrir un archivo y cerrarlo antes de que finalice la función:

package main

import (
	"fmt"
	"os"
)

func main() {
    
    
	file, err := os.Open("file.txt")
	if err != nil {
    
    
		fmt.Println("Error opening file:", err)
		return
	}

	defer func() {
    
    
		err := file.Close()
		if err != nil {
    
    
			fmt.Println("Error closing file:", err)
		}
	}()

	// 使用文件进行操作
	// ...

	fmt.Println("File operations completed")
}

En el código anterior, usamos la palabra clave aplazar para retrasar el cierre del archivo y garantizar que el archivo se cierre después de que se ejecute la función. Esto evita pérdidas de recursos causadas por olvidarse de cerrar el archivo.

3. puntero

Pregunta de la entrevista: ¿Cuál es el papel de los punteros en el lenguaje Go? Dé un ejemplo usando punteros.

respuesta:

Un puntero es una variable que almacena la dirección de memoria de otra variable. A través del puntero, podemos acceder directamente y modificar el valor de la variable en lugar de copiar la variable.

Los punteros son útiles para pasar grandes estructuras de datos y compartir datos entre funciones.

ejemplo de código

Aquí hay un ejemplo usando punteros, intercambiando los valores de dos variables:

package main

import "fmt"

func swap(a, b *int) {
    
    
    temp := *a
    *a = *b
    *b = temp
}

func main() {
    
    
    x := 10
    y := 20
    fmt.Println("Before swap:", x, y)
    swap(&x, &y)
    fmt.Println("After swap:", x, y)
}

En el código anterior, definimos una función de intercambio que recibe dos punteros como parámetros e intercambia los valores de las dos variables a través de los punteros. En la función principal, obtenemos el puntero de la variable a través del operador de dirección &, y pasamos el puntero a la función de intercambio. Mediante el uso de punteros, logramos el intercambio de valores de variables.

4.mapa

¿Qué es el mapa en el idioma Go? Por favor, dé un ejemplo usando el mapa.

respuesta:

Un mapa es una colección desordenada de pares clave-valor, también conocida como diccionario. Las claves en un mapa deben ser únicas, mientras que los valores pueden repetirse. Map proporciona operaciones de búsqueda e inserción rápidas, adecuadas para escenarios que necesitan recuperar rápidamente valores basados ​​​​en claves.

Ejemplo de código:

Aquí hay un ejemplo usando un mapa para almacenar información de calificaciones de los estudiantes:

package main

import "fmt"

func main() {
    
    
	// 创建一个map,键为学生姓名,值为对应的成绩
	grades := make(map[string]int)

	// 添加学生的成绩
	grades["Alice"] = 90
	grades["Bob"] = 85
	grades["Charlie"] = 95

	// 获取学生的成绩
	aliceGrade := grades["Alice"]
	bobGrade := grades["Bob"]
	charlieGrade := grades["Charlie"]

	// 打印学生的成绩
	fmt.Println("Alice's grade:", aliceGrade)
	fmt.Println("Bob's grade:", bobGrade)
	fmt.Println("Charlie's grade:", charlieGrade)
}

En el código anterior, usamos la función make para crear un mapa con claves de tipo string y valores de tipo int. Luego, agregamos la información de calificación del estudiante por clave y obtenemos la calificación del estudiante por clave. Al usar el mapa, podemos encontrar rápidamente las calificaciones correspondientes en función de los nombres de los estudiantes.

Tenga en cuenta que el mapa no está ordenado y el orden del mapa puede ser diferente para cada iteración.

5. Recorrido ordenado del mapa

El mapa no está ordenado y el orden de cada iteración del mapa puede ser diferente. ¿Qué debo hacer si necesito recorrer el mapa en un orden específico?

respuesta:

En el lenguaje Go, los mapas no están ordenados y el orden de cada iteración del mapa puede ser diferente. Si necesita recorrer el mapa en un orden específico, puede seguir los siguientes pasos:

  1. Crea un segmento para contener las claves del mapa.
  2. Iterar sobre el mapa, almacenando claves en segmentos.
  3. Ordenar rebanadas.
  4. Según el orden de las claves ordenadas, recorra el mapa y acceda al valor correspondiente.

Código de muestra:

El siguiente es un código de muestra que muestra cómo recorrer el mapa en orden ascendente por clave:

package main

import (
	"fmt"
	"sort"
)

func main() {
    
    
	m := map[string]int{
    
    
		"b": 2,
		"a": 1,
		"c": 3,
	}

	keys := make([]string, 0, len(m))
	for k := range m {
    
    
		keys = append(keys, k)
	}

	sort.Strings(keys)

	for _, k := range keys {
    
    
		fmt.Println(k, m[k])
	}
}

En el código anterior, creamos un mapa mque contiene pares clave-valor. Luego, creamos un segmento keyse iteramos sobre el mapa para almacenar las claves en el segmento. A continuación, ordenamos el segmento usando sort.Stringsuna función para ordenar el segmento en orden ascendente. Finalmente, iteramos a través del mapa en orden de claves ordenadas y accedemos a los valores correspondientes.

A través de los pasos anteriores, podemos recorrer el mapa en un orden específico y acceder a los pares clave-valor correspondientes. Tenga en cuenta que aquí se usa la ordenación ascendente, si necesita la ordenación descendente, puede usar sort.Sort(sort.Reverse(sort.StringSlice(keys)))la ordenación.

6. Rebanadas y arreglos

¿Cuál es la diferencia entre segmento y matriz en el lenguaje Go? Dé un ejemplo usando slice.

respuesta:

En el lenguaje Go, tanto las matrices como los segmentos se utilizan para almacenar un conjunto de elementos del mismo tipo. Su diferencia radica en la longitud fija y flexible. Los arreglos son de longitud fija mientras que los cortes son de longitud variable.

Ejemplo de código:

Aquí hay un ejemplo usando sectores que demuestra cómo agregar elementos a un sector:

package main

import "fmt"

func main() {
    
    
	// 创建一个切片
	numbers := []int{
    
    1, 2, 3, 4, 5}

	// 向切片中添加元素
	numbers = append(numbers, 6)
	numbers = append(numbers, 7, 8, 9)

	// 打印切片的内容
	fmt.Println(numbers)
}

En el código anterior, []intcreamos un segmento usando sintaxis numberse inicializamos algunos números enteros. Luego usamos appendfunciones para agregar elementos a la rebanada. Mediante el uso de sectores, podemos agregar y eliminar elementos dinámicamente sin especificar la longitud del sector por adelantado.

Cabe señalar que el segmento es una encapsulación basada en matriz, lo que proporciona una operación y flexibilidad más convenientes. La capa inferior del segmento es un puntero a la matriz, que contiene la información de longitud y capacidad del segmento.

Lo anterior trata sobre la diferencia entre sectores y matrices en el lenguaje Go y ejemplos del uso de sectores. Slice es una estructura de datos de uso común en el lenguaje Go, que proporciona una longitud y un modo de operación más flexibles, y es adecuado para recopilaciones de datos que cambian dinámicamente.

7. Rebanar para eliminar elementos

¿Cómo eliminar los datos en el segmento?

respuesta

Para eliminar datos de un segmento, puede usar 切片的切片操作o usar la función integrada appendpara hacerlo. Aquí hay dos métodos comunes:

1. Operaciones de rebanado usando rebanadas:

Con la operación de división de una división, los datos de una división se pueden eliminar especificando la posición de índice del elemento que se eliminará.

Por ejemplo, para eliminar el tercer elemento de una división, use la operación de división de la división para dividir la división en dos y eliminar el tercer elemento del medio.

package main

import "fmt"

func main() {
    
    
    numbers := []int{
    
    1, 2, 3, 4, 5}

    // 移除切片中的第三个元素
    indexToRemove := 2
    numbers = append(numbers[:indexToRemove], numbers[indexToRemove+1:]...)

    fmt.Println(numbers) // 输出: [1 2 4 5]
}

En el código anterior, usamos la operación de corte del segmento para dividir el segmento en dos partes: la parte que numbers[:indexToRemove]representa la parte desde el principio hasta antes del elemento que se eliminará, y la numbers[indexToRemove+1:]parte que representa la parte posterior al elemento que se eliminará. el fin. Luego, usamos appenduna función para volver a conectar las dos partes, implementando así la operación de quitar elementos.

2. Usa appendla función:

Otra forma es usar appenduna función que vuelva a ensamblar las partes antes y después del elemento que se eliminará en una nueva división. Este enfoque es más adecuado para situaciones en las que se desconoce la posición de índice del elemento que se va a eliminar.

package main

import "fmt"

func main() {
    
    
	numbers := []int{
    
    1, 2, 3, 4, 5}

	// 移除切片中的元素3
	elementToRemove := 3
	for i := 0; i < len(numbers); i++ {
    
    
		if numbers[i] == elementToRemove {
    
    
			numbers = append(numbers[:i], numbers[i+1:]...)
			break
		}
	}
	fmt.Println(numbers) // 输出: [1 2 4 5]
}             

En el código anterior, usamos forun bucle para recorrer el segmento y encontrar la posición de índice del elemento que se eliminará. Una vez que se encuentra un elemento coincidente, usamos appenduna función para volver a conectar la parte antes y después del elemento que se va a eliminar, implementando así la operación de eliminar el elemento.

appendLa operación de eliminar datos en un segmento se puede implementar ya sea mediante la operación de división del segmento o mediante una función.

8. entrar en pánico y recuperarse

¿Cuáles son las funciones de pánico y recuperación en el lenguaje Go? Dé un ejemplo del uso de pánico y recuperación.

respuesta:

Panic y recovery son mecanismos para manejar excepciones en el lenguaje Go. Cuando el programa encuentra un error que no se puede manejar, puede usar panic para generar una excepción e interrumpir la ejecución normal del programa. Y la recuperación se usa para capturar y manejar la excepción causada por pánico, para que el programa pueda continuar ejecutándose.

Ejemplo de código:

Aquí hay un ejemplo usando panic y recovery para manejar la división por cero:

package main

import "fmt"

func divide(a, b int) int {
    
    
	defer func() {
    
    
		if err := recover(); err != nil {
    
    
			fmt.Println("Error:", err)
		}
	}()

	if b == 0 {
    
    
		panic("division by zero")
	}
  
	return a / b
}

func main() {
    
    
	result := divide(10, 0)
	fmt.Println("Result:", result)
}

Los resultados de la ejecución son los siguientes:
Error: division by zero Result: 0

En el código anterior, hemos definido una dividefunción para realizar la operación de división. En la función, usamos panicla palabra clave generar una excepción, cuando la división por cero, generará una excepción de "división por cero".

Luego, usamos defery recoverpara capturar y manejar esta excepción e imprimir el mensaje de error. Al usarlo recover, podemos evitar que el programa se bloquee debido a una excepción, pero podemos continuar ejecutando el código posterior.

9. Bloqueo mutex

¿Qué es un mutex (Mutex)? ¿Cómo usar mutex para proteger los recursos compartidos en el lenguaje Go?

respuesta:

Un mutex es un mecanismo de sincronización comúnmente utilizado en la programación concurrente para proteger el acceso a los recursos compartidos.

En el lenguaje Go, puede usar el tipo Mutex en el paquete de sincronización para implementar un mutex. Al llamar al método Lock para adquirir un bloqueo para proteger el acceso a los recursos compartidos y luego llamar al método Unlock para liberar el bloqueo después de usar el recurso compartido.

Ejemplo de código:

package main

import (
	"fmt"
	"sync"
)

var (
	counter int
	mutex   sync.Mutex
)

func increment() {
    
    
	mutex.Lock()
	counter++
	mutex.Unlock()
}

func main() {
    
    
	var wg sync.WaitGroup
	for i := 0; i < 1000; i++ {
    
    
		wg.Add(1)
		go func() {
    
    
			defer wg.Done()
			increment()
		}()
	}
	wg.Wait()

	fmt.Println("Counter:", counter)
}

En el código anterior, definimos un contador de variable global y un mutex mutex de tipo sync.Mutex. En la función de incremento, usamos mutex.Lock() para adquirir el bloqueo, incrementamos el contador y luego usamos mutex.Unlock() para liberar el bloqueo. Mediante el uso de un mutex, garantizamos la seguridad del acceso simultáneo al mostrador.

10. Girar

¿Explicar el estado de espín en la programación concurrente?

respuesta:

El estado de giro es un estado en la programación concurrente, lo que significa que cuando un subproceso o proceso espera que se cumpla una determinada condición, no entrará en un estado inactivo o bloqueado, sino que realizará una espera ocupada comprobando continuamente si se cumple la condición. .

En el estado de giro, el subproceso ejecutará repetidamente un ciclo de espera ocupado hasta que se cumpla la condición o se alcance un cierto tiempo de espera. Este método puede reducir la sobrecarga del cambio de subprocesos y mejorar el rendimiento de la simultaneidad. Sin embargo, el estado de rotación también puede generar una pérdida de recursos de la CPU, ya que el subproceso seguirá ocupando el intervalo de tiempo de la CPU, incluso si no se ha cumplido la condición.

Los estados de espín se utilizan normalmente en las siguientes situaciones:

  • En un sistema multiprocesador, espere a que se libere un recurso compartido para evitar la sobrecarga de la conmutación de subprocesos.
  • Durante tiempos de espera cortos, la condición deseada se cumple rápidamente, evitando la sobrecarga de entrar en un estado de bloqueo.

Cabe señalar que el uso de estados de giro debe usarse con precaución y debe evaluarse de acuerdo con escenarios y condiciones específicos. El uso de un estado de giro puede desperdiciar una gran cantidad de recursos de la CPU si el tiempo de giro es demasiado largo o si es poco probable que la condición se cumpla pronto. En este caso, es más adecuado utilizar el bloqueo o la espera latente.

En resumen, el estado de espín es una técnica de programación concurrente que no duerme ni se bloquea mientras espera que se cumpla una condición. Puede reducir la sobrecarga de la conmutación de subprocesos, pero necesita equilibrar el uso de los recursos de la CPU y la duración del tiempo de espera.

11. Operaciones atómicas y bloqueos

¿Cuál es la diferencia entre operaciones atómicas y bloqueos?

Las operaciones atómicas y los bloqueos son dos mecanismos de sincronización comúnmente utilizados en la programación concurrente, sus diferencias son las siguientes:

  1. Ámbito de acción:

    • Operaciones atómicas: una operación atómica es una operación básica que se puede realizar en el nivel de una sola instrucción para garantizar la atomicidad de la operación. Las operaciones atómicas generalmente se usan para leer, escribir o modificar variables compartidas para garantizar la integridad de la operación.
    • Bloqueo: un bloqueo es un mecanismo de sincronización de nivel superior que se utiliza para proteger el acceso a las secciones críticas. Los bloqueos se pueden usar para limitar el acceso simultáneo a los recursos compartidos para garantizar la seguridad de los subprocesos.
  2. Cómo utilizar:

    • Operaciones atómicas: las operaciones atómicas se implementan a través de instrucciones de hardware o funciones de operaciones atómicas específicas que se pueden aplicar directamente a variables o ubicaciones de memoria sin código adicional.
    • Bloqueo: el bloqueo se implementa a través del mecanismo de bloqueo proporcionado por el lenguaje de programación, y el método relacionado o la declaración del bloqueo deben usarse explícitamente para proteger el acceso a la sección crítica.
  3. granularidad:

    • Operaciones atómicas: las operaciones atómicas suelen ser operaciones en una sola variable o ubicación de memoria que se pueden sincronizar en un nivel muy detallado.
    • Bloqueos: los bloqueos se utilizan generalmente para sincronizar el acceso a un fragmento de código o un conjunto de operaciones, y pueden controlar secciones críticas de mayor granularidad.
  4. Sobrecarga de rendimiento:

    • Operaciones atómicas: las operaciones atómicas generalmente tienen una sobrecarga de rendimiento más baja porque se implementan a nivel de hardware sin mecanismos de sincronización adicionales.
    • Bloqueos: los bloqueos generalmente tienen una sobrecarga de alto rendimiento porque requieren operaciones como el cambio de contexto y la sincronización de subprocesos.

En resumen, las operaciones atómicas y los bloqueos son dos mecanismos de sincronización diferentes para tratar los problemas de sincronización en la programación concurrente. Las operaciones atómicas son adecuadas para operaciones de lectura y escritura en una sola variable, con una sobrecarga de bajo rendimiento. Los bloqueos son adecuados para sincronizar el acceso a un fragmento de código o un conjunto de operaciones, y tienen una sobrecarga de rendimiento superior. La elección de utilizar operaciones atómicas o bloqueos depende de escenarios y requisitos específicos.

Cabe señalar que las operaciones atómicas generalmente se usan para operaciones simples de lectura y escritura en variables compartidas, mientras que los bloqueos son más adecuados para operaciones complejas y protección de acceso a secciones críticas. Cuando se diseñan programas concurrentes, es necesario seleccionar un mecanismo de sincronización adecuado en función de las necesidades específicas y los requisitos de rendimiento.

12.Gorutina

¿Qué es goroutine en el lenguaje Go? Dé un ejemplo usando goroutines.

respuesta:

Goroutine es una unidad de ejecución concurrente liviana en lenguaje Go, que puede ejecutar múltiples goroutines al mismo tiempo sin administrar explícitamente el ciclo de vida de los hilos. Goroutine está programado por el tiempo de ejecución de Go (tiempo de ejecución), que puede realizar una ejecución paralela en programación concurrente.

Ejemplo de código:

Aquí hay un ejemplo usando goroutines para calcular la secuencia de Fibonacci:

package main

import (
    "fmt"
    "sync"
)

func fibonacci(n int, wg *sync.WaitGroup) {
    
    
    defer wg.Done()

    x, y := 0, 1
    for i := 0; i < n; i++ {
    
    
        fmt.Println(x)
        x, y = y, x+y
    }
}

func main() {
    
    
    var wg sync.WaitGroup
    wg.Add(2)

    go fibonacci(10, &wg)
    go fibonacci(5, &wg)

    wg.Wait()
}

En el código anterior, usamos la palabra clave go para iniciar dos gorutinas para calcular los primeros 10 y los primeros 5 números de la secuencia de Fibonacci, respectivamente. Al usar goroutines, podemos ejecutar estas dos tareas computacionales en paralelo sin crear y administrar subprocesos explícitamente.

13. Canal

¿Qué es un canal en el lenguaje Go? Dé un ejemplo del uso de canales.

respuesta:

Los canales son un mecanismo de comunicación y sincronización entre rutinas. Los canales proporcionan una forma segura y bloqueada de enviar y recibir datos. A través de canales, se puede realizar la transferencia de datos y la sincronización entre múltiples rutinas.

Ejemplo de código:

Aquí hay un ejemplo usando canales para calcular la suma de dos números:

package main

import "fmt"

func sum(a, b int, c chan int) {
    
    
    result := a + b
    c <- result // 将结果发送到通道
}

func main() {
    
    
    // 创建一个整型通道
    c := make(chan int)

    // 启动一个goroutine来计算两个数的和
    go sum(10, 20, c)

    // 从通道接收结果
    result := <-c

    fmt.Println("Sum:", result)
}

En el código anterior, definimos una función de suma para calcular la suma de dos números y enviar el resultado al canal c. En la función principal, creamos un canal entero c y luego comenzamos una gorutina para ejecutar la función de suma y enviar el resultado al canal. Finalmente, tomamos la suma calculada y la imprimimos al recibir el resultado del canal.

Mediante el uso de canales, logramos la transferencia de datos y la sincronización entre rutinas. En el ejemplo, el canal c se utiliza para enviar los resultados de los cálculos de la gorutina a la gorutina principal, realizando la transferencia y sincronización de datos.

14.seleccionar

¿Qué es la instrucción select en el lenguaje Go? Dé un ejemplo usando la instrucción select.

respuesta:

La declaración de selección es un mecanismo en el lenguaje Go para manejar operaciones de canal. Puede monitorear las operaciones de lectura y escritura de múltiples canales al mismo tiempo y ejecutar la operación correspondiente cuando cualquiera de los canales está listo.

Ejemplo de código:

Aquí hay un ejemplo que usa una declaración de selección para recibir datos de dos canales:

package main

import "fmt"

func main() {
    
    
    ch1 := make(chan int)
    ch2 := make(chan int)

    go func() {
    
    
        ch1 <- 10
    }()

    go func() {
    
    
        ch2 <- 20
    }()

    select {
    
    
    case num := <-ch1:
        fmt.Println("Received from ch1:", num)
    case num := <-ch2:
        fmt.Println("Received from ch2:", num)
    }
}

En el código anterior, creamos dos canales enteros ch1 y ch2. Luego, iniciamos dos gorutinas para enviar datos a los canales ch1 y ch2 respectivamente. En la rutina principal, usamos la instrucción select para monitorear las operaciones de lectura de estos dos canales y realizamos la operación correspondiente cuando cualquiera de los canales está listo. En el ejemplo, recibimos datos del canal listo y los imprimimos.

Al usar la declaración de selección, podemos implementar operaciones simultáneas en múltiples canales y realizar las operaciones correspondientes de acuerdo con los canales listos. Esto es muy útil cuando se trata de tareas concurrentes.

15. Corrutinas y canales

¿Cómo logra el lenguaje Go la concurrencia a través de gorutina y canal? Dé un ejemplo de programación concurrente.

respuesta:

Go language logra la concurrencia a través de goroutine y channel. Una gorutina es un subproceso ligero que puede ejecutar varias gorutinas al mismo tiempo sin administrar explícitamente el ciclo de vida del subproceso.

Un canal es un conducto para la comunicación entre rutinas. Aquí hay un ejemplo simple de programación concurrente, calculando la secuencia de Fibonacci:

ejemplo de código

package main

import "fmt"

func fibonacci(n int, c chan int) {
    
    
    x, y := 0, 1
    for i := 0; i < n; i++ {
    
    
        c <- x
        x, y = y, x+y
    }
    close(c)
}

func main() {
    
    
    c := make(chan int)
    go fibonacci(10, c)
    for num := range c {
    
    
        fmt.Println(num)
    }
}

En el código anterior, usamos goroutine para iniciar una función para calcular la secuencia de Fibonacci y comunicarnos a través del canal. La función principal recibe el resultado del cálculo del canal y lo imprime. A través de la combinación de gorutina y canal, hemos realizado la función de calcular simultáneamente la secuencia de Fibonacci.

16. tiempo de ejecución

¿Para qué sirve el paquete de tiempo de ejecución en el lenguaje Go? Dé un ejemplo usando el paquete de tiempo de ejecución.

respuesta:

El paquete de tiempo de ejecución es el sistema de tiempo de ejecución del lenguaje Go, que proporciona funciones para interactuar y controlar el sistema subyacente. Contiene funciones y variables relacionadas con la gestión de la memoria, la recolección de basura, la programación de rutinas, etc.

Ejemplo de código:

Aquí hay un ejemplo que usa el paquete de tiempo de ejecución para obtener la cantidad de rutinas actuales:

package main

import (
    "fmt"
    "runtime"
)

func main() {
    
    
    num := runtime.NumGoroutine()
    fmt.Println("Number of goroutines:", num)
}

17. Recolección de basura

¿Cómo funciona la recolección de basura en el lenguaje Go? Dé un ejemplo usando la recolección de basura.

respuesta:

El recolector de basura (Garbage Collector) en el lenguaje Go es un mecanismo para administrar automáticamente la memoria, que se utiliza para recuperar la memoria que ya no se usa. El recolector de basura detecta automáticamente los objetos que ya no se utilizan y libera el espacio de memoria que ocupan.

ejemplo de código

Aquí hay un ejemplo usando la recolección de basura, creando una gran cantidad de objetos temporales:

package main

import (
	"fmt"
	"runtime"
	"time"
)

func createObjects() {
    
    
	for i := 0; i < 1000000; i++ {
    
    
		_ = make([]byte, 1024)
	}
}

func main() {
    
    
	createObjects()
	time.Sleep(time.Second) // 等待垃圾回收器执行

	var stats runtime.MemStats
	runtime.ReadMemStats(&stats)
	fmt.Println("Allocated memory:", stats.Alloc)
}

Imprimir resultado:
Allocated memory: 77344

En el código anterior, creamos muchos objetos temporales a través del ciclo. Luego, usamos time.Sleepla función para esperar a que se ejecute el recolector de basura. Finalmente, usamos runtime.ReadMemStatsla función para leer las estadísticas de la memoria e imprimir el tamaño de la memoria asignada.

Al usar el recolector de basura, podemos administrar la memoria automáticamente y evitar liberar manualmente objetos que ya no se usan. El recolector de basura recuperará automáticamente la memoria no utilizada en el momento apropiado, mejorando así el rendimiento y la confiabilidad del programa.

Resumir

Cuando respondemos preguntas de entrevistas, no podemos memorizar ensayos estereotipados secamente. Debemos combinar escenarios de aplicación, preferiblemente con proyectos que hayamos hecho en el pasado, para comunicarnos con los entrevistadores.

Aunque estas preguntas de escenarios no requieren que rompamos el código a mano, aún debemos estar familiarizados con las ideas de solución y los métodos clave.

Este artículo no solo brinda preguntas y respuestas comunes de entrevistas, sino que también brinda escenarios de aplicación de estos puntos de conocimiento, y también brinda ideas para resolver estos problemas y proporciona códigos clave combinados con estas ideas. Estos segmentos de código se pueden ejecutar directamente desde CV al local, y todos tienen comentarios claramente escritos, invita a todos a practicar, no memorices ensayos estereotipados de memoria.

Finalmente, la organización no es fácil, y la originalidad es aún más difícil. ¡Tus me gusta, comentarios y reenvíos son el mayor apoyo para mí!

Busque en toda la web: 王中阳Go, para obtener más información sobre las preguntas de la entrevista.

Supongo que te gusta

Origin blog.csdn.net/w425772719/article/details/131418035
Recomendado
Clasificación