Go语言高级编程

324课时
1.4K学过
8分

2. 第2章 CGO编程

引言

2.1 快速入门

2.1.1 最简CGO程序

2.1.2 基于C标准库函数输出字符串

2.1.3 使用自己的C函数

2.1.4 C代码的模块化

2.1.5 用Go重新实现C函数

2.1.6 面向C接口的Go编程

2.2 CGO基础

2.2.1 import "C"语句

2.2.2 #cgo语句

2.2.3 build tag 条件编译

2.3 类型转换

2.3.1 数值类型

2.3.2 Go 字符串和切片

2.3.3 结构体、联合、枚举类型

2.3.4 数组、字符串和切片

2.3.5 指针间的转换

2.3.6 数值和指针的转换

2.3.7 切片间的转换

2.4 函数调用

2.4.1 Go调用C函数

2.4.2 C函数的返回值

2.4.3 void函数的返回值

2.4.4 C调用Go导出函数

2.5 内部机制

2.5.1 CGO生成的中间文件

2.5.2 Go调用C函数

2.5.3 C调用Go函数

2.6 实战: 封装qsort

2.6.1 认识qsort函数

2.6.2 将qsort函数从Go包导出

2.6.3 改进:闭包函数作为比较函数

2.6.4 改进:消除用户对unsafe包的依赖

2.7 CGO内存模型

2.7.1 Go访问C内存

2.7.2 C临时访问传入的Go内存

2.7.3 C长期持有Go指针对象

2.7.4 导出C函数不能返回Go内存

2.8 C++ 类包装

2.8.1 C++ 类到 Go 语言对象

2.8.1.1 准备一个 C++ 类

2.8.1.2 用纯C函数接口封装 C++ 类

2.8.1.3 将纯C接口函数转为Go函数

2.8.1.4 包装为Go对象

2.8.2 Go 语言对象到 C++ 类

2.8.2.1 构造一个Go对象

2.8.2.2 导出C接口

2.8.2.3 封装C++对象

2.8.2.4 封装C++对象改进

2.8.3 彻底解放C++的this指针

2.9 静态库和动态库

2.9.1 使用C静态库

2.9.2 使用C动态库

2.9.3 导出C静态库

2.9.4 导出C动态库

2.9.5 导出非main包的函数

2.10 编译和链接参数

2.10.1 编译参数:CFLAGS/CPPFLAGS/CXXFLAGS

2.10.2 链接参数:LDFLAGS

2.10.3 pkg-config

2.10.4 go get 链

2.10.5 多个非main包中导出C函数

课程评价 (0)

请对课程作出评价:
0/300

学员评价

暂无精选评价
20分钟

3.8.2 纯Go方式获取goid

为了便于理解,我们先尝试用纯Go的方式获取goid。使用纯Go的方式获取goid的方式虽然性能较低,但是代码有着很好的移植性,同时也可以用于测试验证其它方式获取的goid是否正确。

每个Go语言用户应该都知道panic函数。调用panic函数将导致Goroutine异常,如果panic在传递到Goroutine的根函数还没有被recover函数处理掉,那么运行时将打印相关的异常和栈信息并退出Goroutine。

下面我们构造一个简单的例子,通过panic来输出goid:

package main

func main() {
	panic("goid")
}

运行后将输出以下信息:

panic: goid

goroutine 1 [running]:
main.main()
	/path/to/main.go:4 +0x40

我们可以猜测Panic输出信息goroutine 1 [running]中的1就是goid。但是如何才能在程序中获取panic的输出信息呢?其实上述信息只是当前函数调用栈帧的文字化描述,runtime.Stack函数提供了获取该信息的功能。

我们基于runtime.Stack函数重新构造一个例子,通过输出当前栈帧的信息来输出goid:

package main

import "runtime"

func main() {
	var buf = make([]byte, 64)
	var stk = buf[:runtime.Stack(buf, false)]
	print(string(stk))
}

运行后将输出以下信息:

goroutine 1 [running]:
main.main()
	/path/to/main.g

因此从runtime.Stack获取的字符串中就可以很容易解析出goid信息:

func GetGoid() int64 {
	var (
		buf [64]byte
		n   = runtime.Stack(buf[:], false)
		stk = strings.TrimPrefix(string(buf[:n]), "goroutine ")
	)

	idField := strings.Fields(stk)[0]
	id, err := strconv.Atoi(idField)
	if err != nil {
		panic(fmt.Errorf("can not get goroutine id: %v", err))
	}

	return int64(id)
}

GetGoid函数的细节我们不再赘述。需要补充说明的是runtime.Stack函数不仅仅可以获取当前Goroutine的栈信息,还可以获取全部Goroutine的栈信息(通过第二个参数控制)。同时在Go语言内部的 net/http2.curGoroutineID 函数正是采用类似方式获取的goid。