Golang中DLL的线程实现与使用
Golang中DLL的线程实现与使用 当使用Go语言编写的DLL被卸载时,默认会创建多个线程。 我该如何在DLL内部终止所有这些线程?
2 回复
据我所知,这是无法实现的,并且这是一个已知问题:runtime: support dlclose with -buildmode=c-shared · Issue #11100 · golang/go · GitHub
更多关于Golang中DLL的线程实现与使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Go语言编写的DLL中,当DLL被卸载时,确实会面临线程管理的问题。以下是几种控制线程终止的方法:
方法1:使用sync.WaitGroup等待所有goroutine结束
//export Initialize
func Initialize() {
// 初始化操作
}
//export Cleanup
func Cleanup() {
// 等待所有goroutine完成
wg.Wait()
}
var wg sync.WaitGroup
//export StartWorker
func StartWorker() {
wg.Add(1)
go func() {
defer wg.Done()
// 工作逻辑
for {
select {
case <-stopChan:
return
default:
// 执行任务
}
}
}()
}
var stopChan = make(chan struct{})
方法2:使用context.Context控制goroutine生命周期
//export StartDLL
func StartDLL() {
ctx, cancel := context.WithCancel(context.Background())
// 启动多个工作goroutine
for i := 0; i < 5; i++ {
go worker(ctx, i)
}
// 保存cancel函数供卸载时调用
globalCancel = cancel
}
//export StopDLL
func StopDLL() {
if globalCancel != nil {
globalCancel()
}
// 等待所有goroutine结束
wg.Wait()
}
func worker(ctx context.Context, id int) {
wg.Add(1)
defer wg.Done()
for {
select {
case <-ctx.Done():
return
default:
// 执行工作
}
}
}
var (
wg sync.WaitGroup
globalCancel context.CancelFunc
)
方法3:使用通道通知所有goroutine退出
//export RunService
func RunService() {
stop := make(chan struct{})
// 启动多个服务goroutine
for i := 0; i < 3; i++ {
go serviceWorker(stop, i)
}
// 保存stop通道
globalStop = stop
}
//export StopService
func StopService() {
close(globalStop)
// 等待所有goroutine结束
for i := 0; i < len(activeWorkers); i++ {
<-doneChan
}
}
func serviceWorker(stop <-chan struct{}, id int) {
activeWorkers++
defer func() {
doneChan <- struct{}{}
}()
for {
select {
case <-stop:
return
default:
// 服务逻辑
}
}
}
var (
activeWorkers int
doneChan = make(chan struct{}, 10)
globalStop chan struct{}
)
方法4:DLL_PROCESS_DETACH处理
//export DllMain
func DllMain(hModule uintptr, dwReason uint32, lpReserved uintptr) bool {
switch dwReason {
case 1: // DLL_PROCESS_ATTACH
runtime.LockOSThread()
case 0: // DLL_PROCESS_DETACH
// 通知所有goroutine停止
if stopAll != nil {
close(stopAll)
}
// 等待清理
cleanupWait.Wait()
case 2: // DLL_THREAD_ATTACH
case 3: // DLL_THREAD_DETACH
}
return true
}
var (
stopAll chan struct{}
cleanupWait sync.WaitGroup
)
//export StartWorkers
func StartWorkers() {
stopAll = make(chan struct{})
for i := 0; i < 5; i++ {
cleanupWait.Add(1)
go func(id int) {
defer cleanupWait.Done()
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for {
select {
case <-stopAll:
return
case <-ticker.C:
// 定期执行任务
}
}
}(i)
}
}
关键注意事项
- 避免在DLL卸载时留下运行中的goroutine
// 错误的做法:直接退出而不等待goroutine
//export Shutdown
func Shutdown() {
// 缺少等待机制,可能导致崩溃
}
// 正确的做法:确保所有goroutine都已停止
//export SafeShutdown
func SafeShutdown() {
// 发送停止信号
close(shutdownChan)
// 等待所有goroutine完成
shutdownWG.Wait()
// 清理资源
cleanupResources()
}
- 使用runtime.LockOSThread()固定系统线程
//export ThreadSafeOperation
func ThreadSafeOperation() {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
// 执行需要固定线程的操作
}
这些方法可以确保在DLL卸载时,所有由Go运行时创建的线程都能被正确终止,避免资源泄漏和程序崩溃。

