【go】简单问答八股,go的理解,接口,锁,channel

谈谈你对go的理解,与其他语言的优势与缺陷

  • 我认为go是一个非常务实的语言,语法简单直接,支持原生高并发,开发速度块,性能强于java,有接近c语言的执行效率(静态编译成机器码)部署简单高效。
  • go的缺陷:
    • 相较于c++不支持函数重载,宏,且GC在超低延迟系统(高频交易)比不过纯手控的c++
    • 错误处理繁琐,频繁if err ,不过go1.13之后引入了错误包装
    • 科学计算和AI领域go的第三方库不如java和python丰富

go的两个接口之间可以存在什么关系?

  • 2个接口有相同的方法列表,那么就是等价的,可以互相赋值
  • 接口可以嵌套

互斥锁与读写锁

  • 互斥锁读写都锁,读写锁,读锁锁写操作,写锁读写都锁
  • 读写锁适合读多写少,互斥锁适合写操作多
package main

import (
	"fmt"
	"sync"
	"time"
)

var (
	num = 0
	// lock = sync.Mutex{} // 使用互斥锁保护共享变量 50ms+
	lock = sync.RWMutex{} // 使用读写锁保护共享变量 100ms+
)

func main() {
	start := time.Now()

	var wg sync.WaitGroup // 定义一个 WaitGroup,用于等待所有 goroutine 完成

	wg.Add(1) // 增加一个计数,表示有一个 goroutine 需要完成
	go func() {
		defer wg.Done() // 在 goroutine 完成时减少计数
		for i := 0; i < 100000; i++ {
			lock.Lock()
			num++
			lock.Unlock()
		}
	}()

	for i := 0; i < 100000; i++ {
		lock.Lock()
		num++
		lock.Unlock()
	}

	wg.Wait() // 等待所有 goroutine 完成

	fmt.Println(num)               // 打印最终的 num 值
	fmt.Println(time.Since(start)) // 打印程序运行耗时
}

// 总结:读写锁适合读多写少
// 互斥锁适合写操作多

channel特点,需要注意什么

Channel 是 goroutine 之间主要通讯方式,通信通过 Channel 完成同步。

  • 类型安全
    一个 Channel 只能传递指定类型的数据,比如 chan int 只能放 int。

  • 分为有缓冲通道和无缓冲通道同步 or 缓冲

    • 无缓冲 channelmake(chan int)):
      • 发送和接收必须同步,发的人等收的人准备好才能发。
    • 有缓冲 channelmake(chan int, 10)):
      • 可以容纳一定数量的数据,发的人可以先存进去,缓冲满了才阻塞。
  • 阻塞性

    • 向 无缓冲channel 发送数据,如果没人接收,会阻塞。
    • 从 无缓冲channel 接收数据,如果没人发送,也会阻塞。
  • 关闭特性

    • close(chan) 可以关闭通道。
    • 关闭后不能再发送数据,但可以继续接收,直到读取完毕
    • 关闭后再次发送数据,会导致panic。关闭已经关闭的通道会panic。(对策:由发送方关闭channel,可通过sync.Once确保只关闭一次)
    • 关闭后接收数据,返回类型零值
  • 多路复用(select)

    • 可以用 select 语句同时等待多个 channel 的操作(类似多路监听)。
  • nil Channel行为

    • 发送到nil channel会永久阻塞

    • 从nil channel接收会永久阻塞

    • 关闭nil channel会panic

无缓冲的通道是同步的,有缓冲的通道是异步


例子
package main

import (
	"fmt"
	"sync"
)

func main() {
	var wg sync.WaitGroup    // 等待组
	var ch chan int          // nil channel
	var ch1 = make(chan int) // 创建无缓冲channel
	fmt.Println(ch, ch1)     // <nil> 0xc000086060
	wg.Add(1)                // 等待组的计数器+1
	go func() {
		// ch <- 15 // 如果给一个nil的channel发送数据会造成永久阻塞
		// <-ch // 如果从一个nil的channel中接收数据也会造成永久阻塞
		ret := <-ch1
		fmt.Println(ret)
		ret = <-ch1 // 从一个已关闭的通道中接收数据,如果缓冲区中为空,则返回该类型的零值
		fmt.Println(ret)
		wg.Done() // 等待组的计数器-1
	}()
	go func() {
		// close(ch1)
		// close(ch) 关闭nil channel会panic
		ch1 <- 15  // 给一个已关闭通道发送数据就会包panic错误
		close(ch1) // 关闭一个已关闭的channel会panic
	}()
	wg.Wait() // 等待组的计数器不为0时阻塞
}


讲解go中new

  • new 用于为给定类型分配内存并进行零值初始化,并返回对应类型的指针

  • 和 make 不同,new 适用于所有类型,而 make 只适用于 slice、map 和 channel

  • new 只做零值初始化不会为内部数据结构做额外准备;而 make 则会初始化返回一个可用对象(不是指针)。

p := new(int)
// p是*int类型,指向一个值为0的int变量
  • 实际开发中,除了极少数需要明确指针的场景,更多是直接使用字面量 &T{} 来创建对象,所以 new 在 Go 中使用得比较

go中的make

  • make 只用于 slice、map、channel

  • make 负责内存分配 + 内部数据结构初始化

  • 返回的是(不是指针)。


Printf、Sprintf、FprintF都是格式化输出,有什么不同?

  • Printf:格式化并输出到标准输出(终端/控制台)。

  • Sprintf:格式化并返回字符串,不直接输出,常用于后续处理。

  • Fprintf:格式化并输出到实现了 io.Writer接口的对象(比如文件、网络连接、缓冲区等)。

io.Writer接口只需要实现一个Write方法

type Writer interface {
    Write(p []byte) (n int, err error)
}

示例:实现一个简单内存Writer

type MemoryWriter struct {
    Buffer []byte
}

func (m *MemoryWriter) Write(p []byte) (n int, err error) {
    m.Buffer = append(m.Buffer, p...)
    return len(p), nil
}

func main() {
    var mw MemoryWriter
    fmt.Fprintf(&mw, "Hello, %s!", "World")
    fmt.Println(string(mw.Buffer)) // 输出: Hello, World!
}

go语言中值传递和地址传递(引用传递)如何运行?有什么区别?举例说明

  • 值传递:参数拷贝到函数内
  • 引用传递: 参数地址拷贝到函数内
  • go没有真正的引用传递,都是值传递
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值