我有这个线程安全队列(在
GoLang
和 Cpp
中实现),我在其中生成超过 100K 线程以将数据推送到共享队列中。在我的笔记本电脑上,我有 8 个核心和 16 个逻辑处理器以及 16GB RAM。
我在
Go
和 C++
上编写了相同的程序。在我的个人机器上,程序在 Go
中的大量线程下运行良好,但是当我使用 cpp
执行程序时,程序崩溃了,我得到 segmentation fault
。我明白这是因为我有 8 个核心,但我正在产生大量线程。但我不明白的是 Go
如何处理这种情况以及为什么 Cpp
没有配置为以相同的方式处理这种情况?
在内部处理线程方面,
Go
与 C++
有何不同?
GoLang
中的以下代码适用于 100K 或以上线程。然而 C++ 甚至无法处理 5K 线程。
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())
}
输出:
size before enqueue: 0
size after enqueue: 100000
size after dequeue: 0
以下 C++ 代码在 100K 线程时出现分段错误
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;
}
我不明白Go是如何处理这种情况的
在 Golang 中,实际系统线程和代码之间有一个层,即运行时调度程序。该调度程序保留一个实际操作系统线程的线程池,用于执行线程代码。
为什么 Cpp 没有配置为以相同的方式处理这种情况?
因为C++线程是操作系统线程。如果需要,您必须创建一个线程池 - 或者使用协程或异步 - 或执行策略。