マルチスレッドシナリオでのスタックメモリ例外
現在の関数のリソースを子スレッドで使用しようとするのは非常に危険ですが、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 を変更または読み込むときにメモリ エラーが発生します。実際には、これはスタック メモリ例外です。スタック メモリは計り知れない問題を引き起こす可能性があるため、これは非常に危険です。このコードにはジョブ スレッドが 1 つしかないため、ここでエラーが報告され、タスク関数でクラッシュします。ただし、コードではパラメーター 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;
}