C++ マルチスレッド シナリオで変数を早期にリリースすると、スタック メモリ例外が発生する

マルチスレッドシナリオでのスタックメモリ例外

現在の関数のリソースを子スレッドで使用しようとするのは非常に危険ですが、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;
}

おすすめ

転載: blog.csdn.net/weixin_44477424/article/details/132284340