다중 스레드 시나리오에서 스택 메모리 예외
자식 스레드에서 현재 함수의 리소스를 사용하려고 시도하는 것은 매우 위험 하지만 C++에서는 이를 지원합니다. 따라서 C++에서 이 작업을 수행하면 스택 메모리 예외가 발생할 수 있습니다.
일반 코드
#include <iostream>
#include <thread>
#include <windows.h>
// 线程函数,用于执行具体的任务
void fun(int param)
{
std::cout << __FUNCTION__ << " param:" << param << std::endl;
Sleep(3000);
param = 1;
std::cout << __FUNCTION__ << " param:" << param << std::endl;
}
// 线程函数,用于执行具体的任务
void threadFunction() {
// 在这里执行线程的具体任务
int num = 2;
//在函数中还启动了一个线程
std::thread funThread([num]()
{
fun(num);
});
funThread.detach();
}
int main() {
const int numThreads = 3;
std::thread threads[numThreads];
// 创建并启动多个线程
for (int i = 0; i < numThreads; ++i) {
threads[i] = std::thread(threadFunction);
}
//等待所有线程执行完毕
for (int i = 0; i < numThreads; ++i) {
threads[i].join();
}
Sleep(1000);
std::cout << "All threads have completed." << std::endl;
return 0;
}
위의 내용은 일반적인 멀티스레드 코드입니다.
예외 코드
그러나 멀티스레드 매개변수 전달이 참조 전달로 설정된 경우 아래와 같이 스택 메모리 예외가 발생할 수 있습니다.
#include <iostream>
#include <thread>
#include <windows.h>
// 线程函数,用于执行具体的任务
void fun(int& param)//传参修改为引用传递
{
std::cout << __FUNCTION__ << " param:" << param << std::endl;
Sleep(3000);
param = 1;
std::cout << __FUNCTION__ << " param:" << param << std::endl;
}
// 线程函数,用于执行具体的任务
void threadFunction() {
// 在这里执行线程的具体任务
int num = 2;
//在函数中还启动了一个线程,传参为引用
std::thread funThread([&num]()
{
fun(num);
});
funThread.detach();
}
int main() {
const int numThreads = 3;
std::thread threads[numThreads];
// 创建并启动多个线程
for (int i = 0; i < numThreads; ++i) {
threads[i] = std::thread(threadFunction);
}
//等待所有线程执行完毕
for (int i = 0; i < numThreads; ++i) {
threads[i].join();
}
Sleep(1000);
std::cout << "All threads have completed." << std::endl;
return 0;
}
컴파일은 성공했지만 실행에는 실패했습니다.
실행 결과:
위의 std::thread funThread( &num {fun(num);}); 는 참조로 전달되고 void fun(int& param) 도 참조 매개변수를 사용합니다. 이때 메모리 예외가 발생할 수 있습니다.
들어오는 매개변수 num은 로컬 매개변수이기 때문에 fun에서 param을 수정하거나 읽을 때 num이 해제되었을 수 있는데, 이때 param을 수정하거나 읽을 때 메모리 오류가 발생하게 되는데, 실제로는 스택 메모리 예외이다. 스택 메모리는 말할 수 없는 문제를 일으킬 수 있기 때문에 이는 매우 위험합니다. 이 코드에는 작업 스레드가 하나만 있으므로 여기에 오류가 보고되므로 작업 기능에서 충돌이 발생합니다. 그러나 코드에서 매개변수 param을 읽고 수정할 수도 있어 다른 스레드에서 예외가 발생하므로 이 문제를 찾기가 어려운 경우가 많습니다.
코드 최적화
물론 참조로 전달하면 임시 변수 생성을 피할 수 있다는 장점이 있지만, 변수가 복잡한 객체인 경우에도 메모리 소모를 크게 줄일 수 있습니다. 참조를 사용할 수는 없지만 std::move를 사용하여 변수 소유권을 이전하고 임시 변수 복사를 줄일 수 있습니다.
#include <iostream>
#include <thread>
#include <windows.h>
// 线程函数,用于执行具体的任务
void fun(int&& param)//右值传参
{
std::cout << __FUNCTION__ << " param:" << param << std::endl;
Sleep(3000);
param = 1;
std::cout << __FUNCTION__ << " param:" << param << std::endl;
}
// 线程函数,用于执行具体的任务
void threadFunction() {
// 在这里执行线程的具体任务
int num = 2;
//在函数中还启动了一个线程
std::thread funThread(fun, std::move(num));//使用move传入右值
funThread.detach();
}
int main() {
const int numThreads = 3;
std::thread threads[numThreads];
// 创建并启动多个线程
for (int i = 0; i < numThreads; ++i) {
threads[i] = std::thread(threadFunction);
}
//等待所有线程执行完毕
for (int i = 0; i < numThreads; ++i) {
threads[i].join();
}
Sleep(1000);
std::cout << "All threads have completed." << std::endl;
return 0;
}