不付费免费网站,网页设计基础包括,中国商业数据网,用word怎么做网站✍个人博客#xff1a;Pandaconda-CSDN博客 #x1f4e3;专栏地址#xff1a;http://t.csdnimg.cn/UWz06 #x1f4da;专栏简介#xff1a;在这个专栏中#xff0c;我将会分享 Golang 面试中常见的面试题给大家~ ❤️如果有收获的话#xff0c;欢迎点赞#x1f44d;收藏… ✍个人博客Pandaconda-CSDN博客 专栏地址http://t.csdnimg.cn/UWz06 专栏简介在这个专栏中我将会分享 Golang 面试中常见的面试题给大家~ ❤️如果有收获的话欢迎点赞收藏您的支持就是我创作的最大动力 16. 什么是 sync.Once
sync.Once 是 Go 语言中的一个同步原语用于实现只执行一次的操作。它可以保证在多个 Goroutine 中只执行一次指定的操作即使这个操作被多次调用。
sync.Once 的使用非常简单只需要创建一个 sync.Once 类型的变量然后使用 Do() 方法来指定要执行的操作。Do() 方法会保证指定的操作只会被执行一次无论它被调用多少次。下面是一个简单的例子
var once sync.Once
func setup() {fmt.Println(Performing setup...)
}
func main() {// 在第一次调用时执行 setup 函数once.Do(setup)// 在第二次调用时不执行任何操作once.Do(func() { fmt.Println(This shouldnt be printed.) })
}
在这个例子中我们首先定义了一个 sync.Once 类型的变量 once。然后我们使用 once.Do() 方法来指定要执行的操作。在第一次调用时Do() 方法会执行 setup() 函数输出 Performing setup...。在第二次调用时Do() 方法不会执行任何操作因为 setup() 函数已经被执行过了。 需要注意的是Do() 方法是阻塞的也就是说在第一次调用还没有完成之前后续的调用会被阻塞。这个特性可以用来保证只有一个 Goroutine 执行指定的操作而其他 Goroutine 等待它完成之后再继续执行。此外Do() 方法只会执行一次指定的操作即使在多个 Goroutine 中调用它。这个特性可以用来避免重复初始化等问题。 17. 什么操作叫做原子操作
在并发编程中原子操作是一种不可中断的操作要么全部完成要么全部不完成。这意味着在多线程环境下原子操作可以保证数据的一致性和可靠性防止多个线程同时对同一数据进行操作而导致的竞争条件和数据不一致。
在 Go 语言中sync/atomic 包提供了一些原子操作函数用于在多线程环境中执行原子操作。这些原子操作函数可以确保对共享变量的访问是原子的即不会被其他线程打断。
例如atomic.AddInt64() 函数可以对一个 int64 类型的变量进行原子加法操作。以下是一个简单的示例
package main
import (fmtsync/atomic
)
func main() {var count int64// 对 count 变量进行 100 次原子加法操作for i : 0; i 100; i {atomic.AddInt64(count, 1)}fmt.Println(count:, count)
}
在这个例子中我们首先定义了一个 int64 类型的变量 count并使用 atomic.AddInt64() 函数对它进行 100 次原子加法操作。AddInt64() 函数的第一个参数是一个指向 int64 类型变量的指针它告诉函数要对哪个变量进行原子操作。第二个参数是要添加的值。在这个例子中我们每次添加的值都是 1。最后我们输出 count 变量的值应该是 100。 需要注意的是原子操作函数仅保证对共享变量的访问是原子的但并不能保证对多个变量之间的操作是原子的。如果需要对多个变量进行原子操作可以使用互斥锁或其他同步机制来保证线程安全。 18. Go 原子操作有哪些
Go atomic 包是最轻量级的锁也称无锁结构可以在不形成临界区和创建互斥量的情况下完成并发安全的值替换操作不过这个包只支持 int32/int64/uint32/uint64/uintptr 这几种数据类型的一些基础操作增减、交换、载入、存储等。
概念
原子操作仅会由一个独立的 CPU 指令代表和完成。原子操作是无锁的常常直接通过 CPU 指令直接实现。 事实上其它同步技术的实现常常依赖于原子操作。
使用场景
当我们想要对某个变量并发安全的修改除了使用官方提供的 mutex还可以使用 sync/atomic 包的原子操作它能够保证对变量的读取或修改期间不被其他的协程所影响。
atomic 包提供的原子操作能够确保任一时刻只有一个 goroutine 对变量进行操作善用 atomic 能够避免程序中出现大量的锁操作。
常见操作 增减 Add 载入 Load 比较并交换 CompareAndSwap 交换 Swap 存储 Store
atomic 操作的对象是一个地址你需要把可寻址的变量的地址作为参数传递给方法而不是把变量的值传递给方法。下面将分别介绍这些操作
增减操作
此类操作的前缀为 Add
func AddInt32(addr *int32, delta int32) (new int32)
func AddInt64(addr *int64, delta int64) (new int64)
func AddUint32(addr *uint32, delta uint32) (new uint32)
func AddUint64(addr *uint64, delta uint64) (new uint64)
func AddUintptr(addr *uintptr, delta uintptr) (new uintptr)
需要注意的是第一个参数必须是指针类型的值通过指针变量可以获取被操作数在内存中的地址从而施加特殊的 CPU 指令确保同一时间只有一个 goroutine 能够进行操作。
使用举例
func add(addr *int64, delta int64) {atomic.AddInt64(addr, delta) //加操作fmt.Println(add opts: , *addr)
}
载入操作
此类操作的前缀为 Load
func LoadInt32(addr *int32) (val int32)
func LoadInt64(addr *int64) (val int64)
func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer)
func LoadUint32(addr *uint32) (val uint32)
func LoadUint64(addr *uint64) (val uint64)
func LoadUintptr(addr *uintptr) (val uintptr)
// 特殊类型 Value类型常用于配置变更
func (v *Value) Load() (x interface{}) {}
载入操作能够保证原子的读变量的值当读取的时候任何其他 CPU 操作都无法对该变量进行读写其实现机制受到底层硬件的支持。
使用示例
func load(addr *int64) {fmt.Println(load opts: , atomic.LoadInt64(opts))
}
比较并交换
此类操作的前缀为 CompareAndSwap该操作简称 CAS可以用来实现乐观锁
func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)
func CompareAndSwapInt64(addr *int64, old, new int64) (swapped bool)
func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool)
func CompareAndSwapUint32(addr *uint32, old, new uint32) (swapped bool)
func CompareAndSwapUint64(addr *uint64, old, new uint64) (swapped bool)
func CompareAndSwapUintptr(addr *uintptr, old, new uintptr) (swapped bool)
该操作在进行交换前首先确保变量的值未被更改即仍然保持参数 old 所记录的值满足此前提下才进行交换操作。CAS 的做法类似操作数据库时常见的乐观锁机制。
需要注意的是当有大量的 goroutine 对变量进行读写操作时可能导致 CAS 操作无法成功这时可以利用 for 循环多次尝试。
使用示例
func compareAndSwap(addr *int64, oldValue int64, newValue int64) {if atomic.CompareAndSwapInt64(addr, oldValue, newValue) {fmt.Println(cas opts: , *addr)return}
}
交换
此类操作的前缀为 Swap
func SwapInt32(addr *int32, new int32) (old int32)
func SwapInt64(addr *int64, new int64) (old int64)
func SwapPointer(addr *unsafe.Pointer, new unsafe.Pointer) (old unsafe.Pointer)
func SwapUint32(addr *uint32, new uint32) (old uint32)
func SwapUint64(addr *uint64, new uint64) (old uint64)
func SwapUintptr(addr *uintptr, new uintptr) (old uintptr)
相对于 CAS明显此类操作更为暴力直接并不管变量的旧值是否被改变直接赋予新值然后返回被替换的值。
func swap(addr *int64, newValue int64) {atomic.SwapInt64(addr, newValue)fmt.Println(swap opts: , *addr)
}
存储
此类操作的前缀为 Store
func StoreInt32(addr *int32, val int32)
func StoreInt64(addr *int64, val int64)
func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer)
func StoreUint32(addr *uint32, val uint32)
func StoreUint64(addr *uint64, val uint64)
func StoreUintptr(addr *uintptr, val uintptr)
// 特殊类型 Value类型常用于配置变更
func (v *Value) Store(x interface{})
此类操作确保了写变量的原子性避免其他操作读到了修改变量过程中的脏数据。
func store(addr *int64, newValue int64) {atomic.StoreInt64(addr, newValue)fmt.Println(store opts: , *addr)
}