学习笔记 | Go 高性能 - 无锁编程

简介: 学习笔记 | Go 高性能 - 无锁编程

go01.jpeg

  • 锁是一种常见的同步机制,用来解决多个线程同时访问共享资源导致的数据竞争问题。在高并发场景下,锁的使用可能会成为性能瓶颈,因为线程需要频繁地加锁和释放锁,这会增加上下文切换开销并降低程序的吞吐量。
  • 无锁编程(lock-free programming)是一种并发编程技术,主要用于消除多线程编程中锁操作带来的性能损耗。


lock-free的优势

  • 减少线程阻塞和等待时间
  • 避免线程的优先级反转
  • 提高并发性能
  • 消除竞态条件(race condition)、死锁、饿死等潜在问题
  • 代码更加清晰易懂


CAS (compare and swap) 是原子操作的一种,用于在多线程中实现原子数据交换,避免多线程同时改写某一共享数据时,由于执行顺序不确定性以及中断的不可预知性产生的数据不一致问题。


示例: 栈操作

// Stack 接口

type StackInterface interface{

Push(interface{})

Pop()interface{}

}


  • 有锁: 使用 sync 包

// 互斥锁实现的栈 (有锁编程)

type MutexStack struct{

// 栈元素容器用切片表示

v []interface{}

// 互斥锁

mu sync.Mutex

}


funcNewMutexStack()*MutexStack {

return&MutexStack{v:make([]interface{},0)}

}


func(s *MutexStack)Push(v interface{}){

// 可能同时有多个 goroutine 操作

// 栈元素操作为临界区,需要加锁

s.mu.Lock()

s.v =append(s.v, v)

s.mu.Unlock()

}


func(s *MutexStack)Pop()interface{}{

// 可能同时有多个 goroutine 操作

// 栈元素操作为临界区,需要加锁

s.mu.Lock()

var v interface{}

iflen(s.v)>0{

 v = s.v[len(s.v)-1]

 s.v = s.v[:len(s.v)-1]

}

s.mu.Unlock()

return v

}


  • 无锁: atomic 包

// 栈元素节点

type directItem struct{

next unsafe.Pointer

v    interface{}

}


// CAS 操作实现的栈 (无锁编程)

type LockFreeStack struct{

// 栈元素容器用链表表示

top unsafe.Pointer

// 栈长度

lenuint64

}


funcNewLockFreeStack()*LockFreeStack {

return&LockFreeStack{}

}


func(s *LockFreeStack)Push(v interface{}){

item := directItem{v: v}

var top unsafe.Pointer


for{

 top = atomic.LoadPointer(&s.top)

 item.next = top

 if atomic.CompareAndSwapPointer(&s.top, top, unsafe.Pointer(&item)){

  // 只有 1 个 goroutine 可以执行到这里

  // 栈元素数量 + 1

  atomic.AddUint64(&s.len,1)

  return

 }

}

}


func(s *LockFreeStack)Pop()interface{}{

var top, next unsafe.Pointer

var item *directItem


for{

 top = atomic.LoadPointer(&s.top)

 if top ==nil{

  returnnil

 }

 item =(*directItem)(top)

 next = atomic.LoadPointer(&item.next)

 if atomic.CompareAndSwapPointer(&s.top, top, next){

  // 只有 1 个 goroutine 可以执行到这里

  // 栈元素数量 - 1

  atomic.AddUint64(&s.len,^uint64(0))

  return item.v

 }

}

}


测试结果比较

$ go test -run='^$' -bench=. -count=1 -benchtime=2s


## 输出结果如下

Benchmark_Stack/*performance.LockFreeStack-8            16553013               134.0 ns/op

Benchmark_Stack/*performance.MutexStack-8               20625786               172.5 ns/op

目录
相关文章
|
4月前
|
数据库连接 Go 数据库
Go语言中的错误注入与防御编程。错误注入通过模拟网络故障、数据库错误等,测试系统稳定性
本文探讨了Go语言中的错误注入与防御编程。错误注入通过模拟网络故障、数据库错误等,测试系统稳定性;防御编程则强调在编码时考虑各种错误情况,确保程序健壮性。文章详细介绍了这两种技术在Go语言中的实现方法及其重要性,旨在提升软件质量和可靠性。
75 1
|
4月前
|
数据采集 监控 Java
go语言编程学习
【11月更文挑战第3天】
63 7
|
4月前
|
Unix Linux Go
go进阶编程:Golang中的文件与文件夹操作指南
本文详细介绍了Golang中文件与文件夹的基本操作,包括读取、写入、创建、删除和遍历等。通过示例代码展示了如何使用`os`和`io/ioutil`包进行文件操作,并强调了错误处理、权限控制和路径问题的重要性。适合初学者和有经验的开发者参考。
|
5月前
|
Java 大数据 Go
Go语言:高效并发的编程新星
【10月更文挑战第21】Go语言:高效并发的编程新星
88 7
|
4月前
|
安全 Go 调度
Go语言中的并发编程:解锁高性能程序设计之门####
探索Go语言如何以简洁高效的并发模型,重新定义现代软件开发的边界。本文将深入剖析Goroutines与Channels的工作原理,揭秘它们为何成为实现高并发、高性能应用的关键。跟随我们的旅程,从基础概念到实战技巧,一步步揭开Go并发编程的神秘面纱,让您的代码在多核时代翩翩起舞。 ####
|
5月前
|
Go 数据处理 调度
Go语言中的并发模型:解锁高效并行编程的秘诀
本文将探讨Go语言中独特的并发模型及其在现代软件开发中的应用。通过深入分析 Goroutines 和 Channels,我们将揭示这一模型如何简化并行编程,提升应用性能,并改变开发者处理并发任务的方式。不同于传统多线程编程,Go的并发方法以其简洁性和高效性脱颖而出,为开发者提供了一种全新的编程范式。
|
5月前
|
中间件 Go API
使用Go语言构建高性能RESTful API
在现代软件开发中,RESTful API因其简洁和高效而成为构建网络服务的首选。Go语言以其并发处理能力和高性能著称,是开发RESTful API的理想选择。本文将介绍如何使用Go语言构建RESTful API,包括基础的路由设置、中间件的使用、数据验证、错误处理以及性能优化。通过实际代码示例,我们将展示Go语言在API开发中的强大功能和灵活性。
|
6月前
|
存储 JSON 安全
go语言编程系列(七)
go语言编程系列(七)
|
6月前
|
存储 安全 编译器
go语言编程系列(六)
go语言编程系列(六)
Go语言的条件控制语句及循环语句的学习笔记
本文是Go语言的条件控制语句和循环语句的学习笔记,涵盖了if语句、if-else语句、if嵌套语句、switch语句、select语句以及for循环和相关循环控制语句的使用方法。
Go语言的条件控制语句及循环语句的学习笔记