You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go与C++线程处理机制差异及高线程数崩溃原因问询

Go与C++线程处理机制差异及高线程场景表现不同的原因

问题背景

在配备8核16逻辑处理器、16GB内存的笔记本上,分别用Go和C++实现线程安全队列,创建超10万个线程向共享队列推送数据:

  • Go程序可正常完成队列的读写操作
  • C++程序触发段错误(segmentation fault),甚至无法处理5千个线程的场景

Go实现代码及运行结果

Go代码

package main

import (
    "fmt"
    "math/rand"
    "sync"
)

type ConcurrentQueue struct {
    queue []int32
    lock  sync.Mutex
}

func (q *ConcurrentQueue) Enqueue(item int32) {
    q.lock.Lock()
    defer q.lock.Unlock()
    q.queue = append(q.queue, item)
}

func (q *ConcurrentQueue) Dequeue() int32 {
    q.lock.Lock()
    defer q.lock.Unlock()

    if len(q.queue) == 0 {
        panic("removing from an empty queue")
    }
    item := q.queue[0]
    q.queue = q.queue[1:]
    return item
}

func (q *ConcurrentQueue) Size() int {
    q.lock.Lock()
    defer q.lock.Unlock()
    return len(q.queue)
}

const NUM_THREADS int = 100000

func main() {
    queue := &ConcurrentQueue{
        queue: make([]int32, 0),
    }

    var wgE sync.WaitGroup
    var wgD sync.WaitGroup

    fmt.Println("size before enqueue:", queue.Size())
    for i := 0; i < NUM_THREADS; i++ {
        wgE.Add(1)
        go func() {
            queue.Enqueue(rand.Int31())
            wgE.Done()
        }()
    }

    wgE.Wait()
    fmt.Println("size after enqueue:", queue.Size())

    for i := 0; i < NUM_THREADS; i++ {
        wgD.Add(1)
        go func() {
            queue.Dequeue()
            wgD.Done()
        }()
    }

    wgD.Wait()

    fmt.Println("size after dequeue:", queue.Size())
}

Go运行输出

size before enqueue: 0
size after enqueue: 100000
size after dequeue: 0

C++实现代码及问题表现

C++代码

#include <iostream>
#include <queue>
#include <cstdlib>
#include <thread>
#include <mutex>
#include <condition_variable>

class ThreadSafeQueue
{
    private:
    std::queue<int> q;
    std::mutex qMutex;  
    std::condition_variable m_cond;
    public:
    ThreadSafeQueue() { }
    ThreadSafeQueue(const ThreadSafeQueue &) = delete ;
    ThreadSafeQueue& operator=(const ThreadSafeQueue &) = delete ;
    
    void Enqueue(int val);
    void Dequeue();
    size_t Size();
};

void ThreadSafeQueue::Enqueue(int val)
{
    std::lock_guard<std::mutex> guard(qMutex);
    q.push(val);
}

void ThreadSafeQueue::Dequeue()
{   
    std::unique_lock<std::mutex> lock(qMutex);

    m_cond.wait(lock, [this] {  return !q.empty(); }); 
    
    q.pop();
}

size_t ThreadSafeQueue::Size()
{
    return q.size();
}

void UpdateQueue(ThreadSafeQueue &qObj, int val)
{
    qObj.Enqueue(val);
}
void DeleteQueue(ThreadSafeQueue &qObj)
{
    qObj.Dequeue();
}

int main()
{
    int N;
    ThreadSafeQueue qObj;
    std::cout<<"How many values do you want to insert"<<std::endl;
    std::cin>>N;
    std::cout<<"Before inserting size of queue is "<<qObj.Size()<<std::endl;

    
    std::vector<std::thread> threads;
    for(int i = 0; i < N; i++)
    {
        threads.push_back(std::thread(UpdateQueue, std::ref(qObj), rand()));
    }

    for (auto& th : threads) th.join();
    threads.clear();
        
    std::cout<<"After inserting size of queue is "<<qObj.Size() <<std::endl;
    std::cout<<"Dequeuing using threads"<<std::endl;
    
    for(int i = 0; i < N; i++)
    {
        threads.push_back(std::thread(DeleteQueue, std::ref(qObj)));
    }
    for (auto& th : threads) th.join();
    std::cout<<"After dequeing size of queue is "<<qObj.Size()<<std::endl;
}

C++运行问题

创建10万个线程时直接触发段错误,即使仅创建5千个线程,也会因内存压力过大无法正常运行。


核心差异与原因分析

1. 线程模型:OS线程 vs Goroutine

  • C++:std::thread直接对应操作系统级线程,每个OS线程默认需要几MB的独立栈空间。创建10万个OS线程时,仅栈内存就需要几十GB(按2MB/线程计算,10万*2MB=200GB),远超16GB物理内存上限,直接导致内存耗尽触发段错误。
  • Go:使用的Goroutine是用户态轻量级线程,由Go运行时调度。每个Goroutine初始栈仅几KB,且可动态扩容缩容,10万个Goroutine的栈内存总计仅几百MB,完全在16GB内存的承载范围内。

2. 调度机制:内核调度 vs 用户态调度

  • C++:OS线程由内核调度器管理,线程切换需要完成上下文保存、寄存器刷新、TLB更新等操作,开销极高。大量线程会导致调度资源耗尽,同时内存压力进一步加剧系统崩溃。
  • Go:Go运行时采用M:N调度器,将多个Goroutine映射到少量OS线程(通常与CPU核心数匹配),Goroutine的切换在用户态完成,切换成本极低。即使10万个Goroutine,也只会由少数OS线程承载,避免了内核级调度的高开销和内存爆炸问题。

3. C++代码的额外隐患

C++代码中的Size()方法未加锁,当其他线程正在修改队列时调用size()会引发数据竞争,可能返回错误的队列长度,但这不是崩溃的核心原因。崩溃的根本原因还是OS线程的内存占用过高。


内容的提问来源于stack exchange,提问作者Himanshu Poddar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 23:55:55