C++ 线程与传入参数的一些有趣的测试
其实我更想把这个发到提问区,但是基于篇幅还是算了 = =
使用系统 Windows 7 + Ubuntu 16.04 -> Windows IDE VS2017 -> Ubuntu IDE VSCode
==================================================================================================
昨天在看菜鸟的教程的时候试了一下多线程,菜鸟上面使用的以前标准库里面的pthread.h这个头文件,但是不知道是否是版本问题,各种爆BUG,出现各种错误(pthread.h 这个头文件在Windows上截止到目前还没有找到解决方案),虽然后来使用C++11版本的thread.h头文件没有出现奇怪的东西,但是身为一个求知欲(装逼心理)高到那啥的人,怎么可能会因为这些就放弃,所以我在Linux上测试了一下pthread.h 在 Windows 上测试了一下 thread.h ,呵,还真出现了一些意想不到的事情啊。。。
废话少说,下面进入代码(装逼环节)。。。
==================================================================================================
首先,我们来看一段代码,这段代码是在Ubuntu + pThread.h 测试的--->>>注意,这个代码运行和我想的并不太一样,最后会给出运行结果
#include <iostream>
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <string>
#include <sstream>
using namespace std;
#define _THREAD_NUM 2
#define LINK_MULTYPE(a,b) a##b
void* Debug(void* args)
{
stringstream debug_str;
debug_str <<" -->>Thread : "<<*((string*)args)<<" -> path : "<<&args;
while(true)
{
cout<<debug_str.str()<<endl;
sleep(1); // s
}
}
int main()
{
bool IsStart = true; // 因为数据的输出与我的预想不一样,所以用各种办法测试,这个bool就是其中之一
pthread_t thid[_THREAD_NUM];
for(int i =0;i<_THREAD_NUM;i++)
{
if(IsStart){
string str = "Thread:1";
pthread_create(&thid[i],NULL,Debug,(void*)&(str));
IsStart = false;
cout<<&str<<endl;
}else
{
string str1 = "Thread:2";
pthread_create(&thid[i],NULL,Debug,(void*)&(str1));
cout<<&str1<<endl;
}
}
cout<<"hello world!!!"<<endl;
getchar();
pthread_exit(NULL);
return 0;
}
这段代码很简单,就是创建_THREAD_NUM个线程,并且让他无限输出一些数据,运行结果最后会给出
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
再来看下一段代码,和上面的差不多,不过是使用 Windows + thread.h 运行测试:
#include <iostream>
#include <thread>
#include <Windows.h>
#include <string>
#include <sstream>
using namespace std;
#define _THREAD_NUM 3
void Debug(int* str);
int main()
{
thread thre[_THREAD_NUM];
int tempNum[_THREAD_NUM];
for (int i = 0; i < _THREAD_NUM; i++)
{
thre[i] = thread(Debug, &i);
}
for (int i = 0; i < _THREAD_NUM; i++)
{
thre[i].join();
}
system("pause");
return 0;
}
void Debug(int* str)
{
stringstream debug_str;
debug_str << " Windows c++ thread.h -->>Thread : " << *str << " -> path : " << &str;s
while (true) {
cout << debug_str.str() <<"\r\n"<< endl;
Sleep(1000); // ms
}
}
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Windows + CSharp
using System;
using System.Threading;
namespace Win32ThreadTest
{
class Program
{
private static int _THREAD_NUM = 3;
static void Main(string[] args)
{
Thread[] thre = new Thread[_THREAD_NUM];
for (int i = 0; i < _THREAD_NUM; i++)
{
thre[i] = new Thread(Debug);
thre[i].Start(i);
}
Console.ReadLine();
}
unsafe static void Debug(object o)
{
int temp = (int)o; // 本来想写一行 不过他不让我拆箱 = =
string str = string.Format(" Windows CSharp -> Thread --> Thread : {0} -> path :{1} ", o, Convert.ToString(((int)&temp),16).ToUpper()); // 输出十六进制的内存地址
while (true)
{
Console.WriteLine(str);
Thread.Sleep(1000); // ms
}
}
}
}
行了,以上三位嘉宾已经清楚,各位大佬们可以猜测一下运行结果了 Ha
~
~
~
~
~
~
~
~
运行:
有没有发现一些奇怪的东西混进去了,如果没有,就仔细的看一下输出的参数和对应的地址
在运行一下:::
没错,第一次运行的时候,Linux的数据被替换了,这里在说一下我理解的为什么会发生替换,
以前,还没接触C++的时候就听说了C++的内存机制,他是会在寄存器查找第一个符合的内存地址然后将我们传入的数据指向这块地址,那么看我们上面的代码,我们的参数是在for里面的,也就是说每一次运行结束之后,内存都会清空一次(而这块内存就会重新回到寄存器的开放列表中),而下一次在申请内存的时候,就会再次查找符合的内存地址,但是我们前一个创建的线程并不知道这块内存地址已经发生了更改,他还是会使用原先的内存地址,所以就会发生出现两个相同地址和相同参数的输出发生,而CSharp里面的变量也是一个类,在回收的时候也会调用他的析构函数,并且将其加入到内存的开放列表,但是,熟悉C#的都会知道,c#的析构函数并不会第一时间执行,当然也有可能c#不光有堆,栈,还有临时区域,在一定时间没有使用的时候才会释放掉这个地方的内存(红字纯属瞎猜,有错误请在评论或是私信,我会及时更正)
修改一下代码,加几个sleep(PS:C++我不确定Sleep是不是加到那里)
每一个我都运行了十遍以上,不排除运气好正好撞上十次
结果,Linux + pthread.h 一样会获取到原先的内存地址,Windows + thread.h 成功 , C# 加不加GC 和Thread.Sleep都一样(应该是内部内存管理的功劳)
------------------------------------------------------------------------后续,给出解决方案--------------------------------------------------------------------
把参数设置成公共的吧,为了测试,所以我只是使用的块生命周期的参数,但是其实只要把参数设置为公共参数,防止内存回收,那么pthread.h 和 thread.h 和using System.Threading都差不多。。。
-----------------------------------------------------------------------最后,附上一个带锁的多线程--------------------------------------
#include <iostream>
#include <thread>
#include <Windows.h>
#include <string>
#include <sstream>
#include <mutex> // 这个是锁 lock
using namespace std;
#define _THREAD_NUM 3
mutex sum_lock; // 声明锁
void Debug(int* str);
void Sum(int id);
int num = 0;
int thread_id[_THREAD_NUM];
int main()
{
thread thre[_THREAD_NUM];
for (int i = 0; i < _THREAD_NUM; i++)
{
thread_id[i] = i;
thre[i] = thread(Sum, thread_id[i]);
Sleep(1000);
}
for (int i = 0; i < _THREAD_NUM; i++)
{
thre[i].join();
Sleep(1000);
}
system("pause");
return 0;
}
void Debug(int* str)
{
stringstream debug_str;
debug_str << " Windows c++ thread.h -->>Thread : " << *str << " -> path : " << &str;
while (true) {
cout << debug_str.str() << "\r\n" << endl;
Sleep(1000); // ms
}
}
void Sum(int id)
{
sum_lock.lock(); // 加锁
for (int i = 0; i < 10; i++)
{
num++;
cout << "Thread->" << id << ";NUM->" << num << endl;
Sleep(100);
}
sum_lock.unlock(); // 解锁,此时别的程序就可以进到这里来了,下面那个Sleep会同步执行
Sleep(1000);
}
------------------
-----------------------------------------------------------------------未解决问题------------------------------------------------------------------------------
看上面的某一个输出就知道,thread.h输出的时候有两个地址是不一样的,但是输出出来时,他们获取到的参数一致,这个暂时没有头绪,(脑壳疼)