Golang中大量goroutine导致cgo pthread互斥锁崩溃的问题

Golang中大量goroutine导致cgo pthread互斥锁崩溃的问题 这到底是怎么回事?我已经将ulimit增加到102400等等,但还是不行?!是Go语言有什么问题吗?

package main

/*
#include <pthread.h>
#include <stdlib.h>

pthread_mutex_t lock;

void lock_init() {
    pthread_mutex_init(&lock, NULL);
}

void lock_destroy() {
    pthread_mutex_destroy(&lock);
}

void lock_mutex() {
    pthread_mutex_lock(&lock);
}

void unlock_mutex() {
    pthread_mutex_unlock(&lock);
}
*/
import "C"
import (
//    "runtime"
    "sync"
    "log"
    "time"
)

func main() {
    C.lock_init()
    defer C.lock_destroy()

    var wg sync.WaitGroup
    for i := 0; i < 10000; i++ {
        wg.Add(1)
        go func() {
            C.lock_mutex()
            // Critical section: You can place code here that needs synchronization.
            time.Sleep(300*time.Nanosecond)
            C.unlock_mutex()
            wg.Done()
        }()
    }

    wg.Wait() // Wait for all goroutines to finish


    log.Printf("same thing will crash in 3 second with more iterations")
    time.Sleep(3*time.Second)
    for i := 0; i < 1000000; i++ {
        wg.Add(1)
        go func() {
            C.lock_mutex()
            // Critical section: You can place code here that needs synchronization.
            time.Sleep(300*time.Nanosecond)
            C.unlock_mutex()
            wg.Done()
        }()
    }

    wg.Wait() // Wait for all goroutines to finish

}

更多关于Golang中大量goroutine导致cgo pthread互斥锁崩溃的问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html

5 回复

不。你有任何可行的解决方案吗?

更多关于Golang中大量goroutine导致cgo pthread互斥锁崩溃的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


用于程序间的 Go 语言全局互斥锁锁定

这是如何工作的?

你能在 C 程序中使用 pthread_create 创建更多线程吗?

@Helmut_Wais 关于跨程序的 Go 全局互斥锁锁定。我将内核的 threads-max 设置为 1024000,但在 80000 个 goroutine 时仍然崩溃。如何解决?也尝试将 SetMaxThreads 设置为 1024000,但不起作用。崩溃。

为什么你使用 pthread_mutex_t 而不是 sync.Mutex? 仅仅是为了演示目的吗?

pthread_create 可能因其他原因而失败并返回 EAGAIN,而不仅仅是达到最大进程数。可能是内存或栈空间不足。 除了 ulimit -u 之外,其他限制也可能相关,例如 sysctl kernel.threads-maxsysctl vm.max_map_count

并不是说我读过 Go 的源代码,但我可以想象,持有 pthread 互斥锁的线程上不会调度 goroutine。因此会不断创建新线程,直到 pthread_create 失败。

无论如何,创建 10 万或更多的 pthread 似乎是个坏主意。它们不像 goroutine 那样廉价。

// 代码示例(如果原内容包含)
// 此处应放置原HTML中的Go代码,但当前提供的HTML片段未包含具体的Go代码块。
// 根据要求,所有Go代码必须用```go包裹。

这是典型的Go调度器与C线程互斥锁交互问题。Go的goroutine在C调用期间可能被调度到不同的OS线程,而pthread互斥锁要求加锁和解锁必须在同一线程中执行。

问题分析:

  1. 当goroutine在C.lock_mutex()调用后被调度到另一个OS线程时
  2. 后续的C.unlock_mutex()可能在不同线程中执行,违反pthread互斥锁要求
  3. 大量goroutine加剧了线程迁移的可能性

解决方案:使用runtime.LockOSThread()将goroutine绑定到当前OS线程

package main

/*
#include <pthread.h>
#include <stdlib.h>

pthread_mutex_t lock;

void lock_init() {
    pthread_mutex_init(&lock, NULL);
}

void lock_destroy() {
    pthread_mutex_destroy(&lock);
}

void lock_mutex() {
    pthread_mutex_lock(&lock);
}

void unlock_mutex() {
    pthread_mutex_unlock(&lock);
}
*/
import "C"
import (
    "runtime"
    "sync"
    "log"
    "time"
)

func worker(wg *sync.WaitGroup) {
    runtime.LockOSThread() // 绑定goroutine到当前OS线程
    defer runtime.UnlockOSThread()
    
    C.lock_mutex()
    time.Sleep(300*time.Nanosecond)
    C.unlock_mutex()
    wg.Done()
}

func main() {
    C.lock_init()
    defer C.lock_destroy()

    var wg sync.WaitGroup
    
    // 第一轮测试
    for i := 0; i < 10000; i++ {
        wg.Add(1)
        go worker(&wg)
    }
    wg.Wait()
    
    log.Printf("使用LockOSThread后,第二轮测试不会崩溃")
    time.Sleep(3*time.Second)
    
    // 第二轮测试
    for i := 0; i < 1000000; i++ {
        wg.Add(1)
        go worker(&wg)
    }
    wg.Wait()
    
    log.Printf("所有goroutine执行完成")
}

替代方案:使用Go原生的sync.Mutex(推荐)

package main

import (
    "sync"
    "log"
    "time"
)

func main() {
    var mu sync.Mutex
    var wg sync.WaitGroup
    
    // 第一轮测试
    for i := 0; i < 10000; i++ {
        wg.Add(1)
        go func() {
            mu.Lock()
            time.Sleep(300*time.Nanosecond)
            mu.Unlock()
            wg.Done()
        }()
    }
    wg.Wait()
    
    log.Printf("使用sync.Mutex,第二轮测试不会崩溃")
    time.Sleep(3*time.Second)
    
    // 第二轮测试
    for i := 0; i < 1000000; i++ {
        wg.Add(1)
        go func() {
            mu.Lock()
            time.Sleep(300*time.Nanosecond)
            mu.Unlock()
            wg.Done()
        }()
    }
    wg.Wait()
    
    log.Printf("所有goroutine执行完成")
}

关键点:

  1. Go调度器可能在任何函数调用点切换goroutine到不同OS线程
  2. pthread互斥锁有线程关联性要求
  3. runtime.LockOSThread()确保C调用在同一线程中执行
  4. 优先使用Go原生同步原语,避免cgo线程安全问题
回到顶部