【Linux】线程控制

【Linux】线程控制

文章目录

铺垫:

根据上次所说:Windows中实现了真正的线程,Windows中有线程的系统调用。

1. 线程的创建

pthread_creat()函数

报错!!!

既没有在C语言中没有在C++中,也不是系统调用,编译器不认识。在编译是需要手动指明链接原生线程库

txt 复制代码
功能:创建一个新的线程
原型
    int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *
    (*start_routine)(void*), void *arg);
参数
    thread:返回线程ID
    attr:设置线程的属性,attr为NULL表示使用默认属性
    start_routine:是个函数地址,线程启动后要执行的函数
    arg:传给线程启动函数的参数
返回值:成功返回0;失败返回错误码

错误检查:

  • 传统的一些函数是,成功返回0,失败返回-1,并且对全局变量errno赋值以指示错误。
  • pthreads函数出错时不会设置全局变量errno(而大部分其他POSIX函数会这样做)。而是将错误代码通
    过返回值返回
  • pthreads同样也提供了线程内的errno变量,以支持其它使用errno的代码。对于pthreads函数的错误,
    建议通过返回值业判定,因为读取返回值要比读取线程内的errno变量的开销更小

示例:

c++ 复制代码
void* newthreadrun(void * args)
{
    int cnt = 5;
    while(cnt)
    {
        cnt--;
        std::cout<<"new thread is runing: "<<cnt<<",getpid: "<<getpid()<<std::endl;
        sleep(1);
    }
    return nullptr;
}

int main()
{
    pthread_t tid;
    //---------------创建线程----------------------------
    pthread_create(&tid,nullptr,newthreadrun,(void*)"thread-1");
    //1. &tid:指向一个pthread_t类型的变量,该变量用于接收新创建线程的ID
    //2. nullptr:指向一个pthread_attr_t 类型的变量,该变量用于设置新线程的属性。如果传递nullptr,则新线程的属性为默认属性
    //3. newthreadrun:这是一个函数指针,指向新线程将要执行的函数,该函数的返回类型为void*
    //4. (void*)"thread-1": 这是一个指针,指向新·线程传递参数,如果不需要传参,可以传递nullptr。
   int cnt = 10;
    while(cnt)
    {
        cnt--;
       	std::cout<<"main thread is runing "<<cnt<<",getpid: "<<getpid()<<std::endl;
        sleep(1);
    }
}

线程的ID:

  1. 理解pthread线程中 ---会谈线程id
  • pthread_ create函数会产生一个线程ID,存放在第一个参数指向的地址中。该线程ID和前面说的线程ID
    不是一回事。
  • 前面讲的线程ID属于进程调度的范畴。因为线程是轻量级进程,是操作系统调度器的最小单位,所以需要一个数值来唯一表示该线程。
  • pthread_ create函数第一个参数指向一个虚拟内存单元,该内存单元的地址即为新创建线程的线程ID,
    属于NPTL线程库的范畴。线程库的后续操作,就是根据该线程ID来操作线程的。
  • 线程库NPTL提供了pthread_ self函数,可以获得线程自身的ID:

thread_self()

示例:

c++ 复制代码
void* newthreadrun(void * args)
{
    int cnt = 5;
    while(cnt)
    {
        cnt--;
        std::cout<<"new thread is runing: "<<cnt<<" ,getpid: "
        <<getpid()<<"   mythread id: "<<ToHex(pthread_self())<<std::endl;
        sleep(1);
    }
    return nullptr;
}

int main()
{
    pthread_t tid;
    //---------------创建线程----------------------------
    pthread_create(&tid,nullptr,newthreadrun,(void*)"thread-1");
   	int cnt = 10;
    while(cnt)
    {
        cnt--;
       	std::cout<<"main thread is runing "<<cnt<<",getpid: "<<getpid()
        <<" new thread id: " <<ToHex(tid)<<" main thread id: "
        <<ToHex(pthread_self())<<std::endl;
        sleep(1);
    }
}

新线程和主线程都有各自的唯一ID。

pthread_t 到底是什么类型呢?取决于实现。对于Linux目前实现的NPTL实现而言,pthread_t类型的线程ID,本质就是一个进程地址空间上的一个地址。

新线程和主线程哪个先运行?

不确定,由调度器决定。

线程简单传参

示例:

c++ 复制代码
void* newthreadrun(void * args)
{	
	char* name = (char*)args;
    int cnt = 5;
    while(cnt)
    {
        cnt--;
        std::cout<<name<<" is runing: "<<cnt<<" ,getpid: "
        <<getpid()<<"   mythread id: "<<ToHex(pthread_self())<<std::endl;
        sleep(1);
    }
    return nullptr;
}

int main()
{
    pthread_t tid;
    //---------------创建线程----------------------------
    pthread_create(&tid,nullptr,newthreadrun,(void*)"thread-1");
   	int cnt = 10;
    while(cnt)
    {
        cnt--;
       	std::cout<<"main thread is runing "<<cnt<<",getpid: "<<getpid()
        <<" new thread id: " <<ToHex(tid)<<" main thread id: "
        <<ToHex(pthread_self())<<std::endl;
        sleep(1);
    }
}

主线程退出,新线程会怎样?

示例:

c++ 复制代码
void * newthreadrun(void* args)
{
    int cnt = 10;
    while(cnt--)
    {
        std::cout<<"I am new thread "<<std::endl;
        sleep(1);
    }
    return nullptr;
}
int main()
{
	pthread_t tid;
    pthread_create(&tid,nullptr,newthreadrun,nullptr);
    sleep(1);
    return 0;
}

通过结果我们可以观察到:

主线程退出 == 进程退出 == 所有线程都要退出

  1. 所以往往我们需要将main thread最后退出
  2. 线程也要被"wait",要不然也会出现类似于进程的内存泄露问题

2. 线程等待

线程为什么需要等待?

  • 已经退出的线程,其空间没有被释放,仍然在进程的地址空间内。
  • 创建新的线程不会复用刚才退出线程的地址空间。

pthread_join()

功能:等待线程结束
原型
	int pthread_join(pthread_t thread, void **value_ptr);
参数
    thread:线程ID
    value_ptr:它指向一个指针,后者指向线程的返回值
返回值:成功返回0;失败返回错误码

调用该函数的线程将挂起等待,直到id为thread的线程终止。thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的,总结如下:

  • 如果thread线程通过return返回,value_ ptr所指向的单元里存放的是thread线程函数的返回值。
  1. 如果thread线程被别的线程调用pthread_ cancel异常终掉,value_ ptr所指向的单元里存放的是常数
    PTHREAD_ CANCELED。
  2. 如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参
    数。
  3. 如果对thread线程的终止状态不感兴趣,可以传NULL给value_ ptr参数。

示例:验证等待返回值

c++ 复制代码
void* newthread_1(void* args)
{
    std::cout<<"newthread_1 is runing ..."<<std::endl;
	int *p = (int*)malloc(sizeof(int));
	*p = 1;
	return (void*)p;
}

void* newthread_2(void* args)
{
	std::cout<<"newthread_2 is runing ..."<<std::endl;
	int *p = (int*)malloc(sizeof(int));
	*p = 1;

	pthread_exit((void*)p);
}

void* newthread_3(void* args)
{
	while(true)
	{
		std::cout<<"newthread_3 is runing ..."<<std::endl;
		sleep(1);
	}
	return nullptr;
}
int main()
{
    void* ret = nullptr;
    pthread_t tid;

	//thread 1 return 
    pthread_create(&tid,nullptr,newthread_1,nullptr);
	pthread_join(tid,&ret);
	std::cout<<"newthread_1 return, thread id: "<<tid<<"  return code: "<<*(int*)ret<<std::endl;
	//(long long)ret 实际上是打印指针的地址,而不是指针的指向的值,ret是指向动态分配的整数的指针
	free(ret);

	//thread 2 pthread_exit
	pthread_create(&tid,nullptr,newthread_2,nullptr);
	pthread_join(tid,&ret);
	std::cout<<"newthread_2 exit, thread id: "<<tid<<"  return code: "<<*(int*)ret<<std::endl;
	free(ret);

	//thread 3 pthread_cancel

	pthread_create(&tid,nullptr,newthread_3,nullptr);
	sleep(3);
	pthread_cancel(tid);
	pthread_join(tid,&ret);
	if(ret == PTHREAD_CANCELED)
		std::cout<<"newthread_3 exit, thread id: "<<(long long)tid<<"  return code: "<<" PTHREAD_CANCELED "<<std::endl;
	else
		std::cout<<"newthread_3 exit, thread id: "<<(long long)tid<<"  return code: "<<" nullptr "<<std::endl;
	//free(ret);会出现Segmentation fault 错误,原因:线程被取消,根本就没有返回值,pthread_join函数返回PTHREAD_CANCELED,
	//这意味ret不会是动态分配内存的指针,而是而是PTHREAD_CANCELED的宏值。因此尝试对ret调用free是错误的
   return 0;
}

3. 线程异常

结果:

  • 代码跑完结果对
  • 代码跑完结果错
  • 出异常----多线程中,任何一个线程出现异常,都会导致整个进程退出!

同一个进程内的线程,大部分资源是共享的,地址空间是共享的!!

c++ 复制代码
int global_val = 100;//已初始化全局数据区
void* newthread(void* args)
{
    int cnt = 5;
    while(true)
    {
        global_val++;
        std::cout<<"new thread is run..,global_val:  "<<global_val<<"  &global_val:"<<&global_val<<std::endl;
        sleep(1);
    }
    return nullptr;
}
int main()
{
	pthread_t tid;
   	pthread_create(&tid,nullptr,newthread,nullptr);
    while(true)
    {
        std::cout<<"main thread is run..,global_val:  "<<global_val<<"  &global_val:"<<&global_val<<std::endl;
        sleep(1);
    }
    pthread_join(tid,nullptr);
    sleep(5);
    return 0;
}

从结果可以看出,主线程和新线程共享同一份global_bal,当在新线程中修改global时,主线程也可以看见。

前文中代码,主线程 和 新线程都可以使用ToHex函数,也说明了这一点。

示例:

c++ 复制代码
void* newthread(void* args)
{
    int cnt = 5 ;
    while(cnt--)
    {
        sleep(1);
        if(cnt == 2)
        {
            int *ptr = nullptr;
            *ptr = 100;//野指针---新线程出现异常
        }
        std::cout<<"new thread is run ..."<<std::endl;
    }

    return nullptr;
}
int main()
{
	pthread_t tid;
   	pthread_create(&tid,nullptr,newthread,nullptr);
    //主进程永远不会退出
    while(true)
    {
        sleep(1);
        std::cout<<"main thread is run ..."<<std::endl;
    }
    pthread_join(tid,nullptr);
    sleep(5);
    return 0;
}

通过结果:发现多线程中,任何一个线程出现异常,都会导致整个进程退出!!!

线程是进程的一个执行分支,线程出现异常(dev 0 、野指针),就是进程出异常。

思考:为什么在进行pthread_join()时为什么没有返回线程退出信号,只有退出码?

因为一旦线程收到异常,就会导致整个进程退出,根本就没有机会返回线程退出信号,故pthread_join()不考虑线程异常情况。----处理异常的责任要落在创建该线程的父进程(或主线程)上。

4. 线程退出

  1. 线程函数结束,线程就退出了

  2. exit()函数可以使线程退出吗?

    示例:

    c++ 复制代码
    void* newthread(void* args)
    {
        int cnt = 5;
    	while(cnt--)
        {
            sleep(1);
            std::cout<<"new thread is run..."<<std::endl;
        }
        exit(10);
    	return nullptr;
    }
    int main()
    {
    	pthread_t tid;
    	pthread_create(&tid,nullptr,newthread,nullptr);
    	while(true)
    	{
    		sleep(1);
    		std::cout<<"main thread is run... "<<std::endl;
    	}
    	pthread_join(tid,nullptr);
    	return 0;
    }

exit()不能退出线程,exit()是退出进程的。

  1. pthread_exit()退出线程

    功能:线程终止
    原型
    void pthread_exit(void *value_ptr);
    参数
    value_ptr:value_ptr不要指向一个局部变量。
    返回值:无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身)

[!CAUTION]

需要注意,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。

示例:

C++ 复制代码
void* newthread(void* args)
{
    int cnt = 2;
	while(cnt--)
    {
        sleep(1);
        std::cout<<"new thread is run..."<<std::endl;
    }
    std::cout<<"new thread quit..."<<std::endl;
    pthread_exit((void*)100);
    //需要注意,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能	  //在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。
	return nullptr;
}
int main()
{
	void* ret;
	pthread_t tid;
	pthread_create(&tid,nullptr,newthread,nullptr);
	int cnt = 5;
	while(cnt--)
	{
		sleep(1);
		std::cout<<"main thread is run... "<<std::endl;
	}
	pthread_join(tid,&ret);
	std::cout<<"new thread return inf: "<<(long long)ret<<std::endl;
	std::cout<<"main thread quit ... "<<std::endl;
	return 0;
}

  1. pthread_cancle()

    功能:取消一个执行中的线程
    原型
    int pthread_cancel(pthread_t thread);
    参数
    thread:线程ID
    返回值:成功返回0;失败返回错误码

示例:

C++ 复制代码
void* newthread(void* args)
{
    while(true)
    {
        std::cout<<"new thread is runing ..."<<std::endl;
        sleep(1);
    }
    return (void*)100;
}
int main()
{
    void* ret = nullptr;
    pthread_t tid;
    pthread_create(&tid,nullptr,newthread,nullptr);
	int cnt = 3;
    while(cnt--)
    {
        std::cout<<"main thread is runing ..."<<std::endl;
        sleep(1);
    }
    pthread_cancel(tid);
    //1. 向tid线程发送撤销请求
    //2. 在确保线程已经启动,下终止线程。---向线程发送撤销请求
    pthread_join(tid,&ret);//等待tid线程,释放资源,回收返回信息。
    std::cout<<"new thread ret_inf: "<<(long long)ret<<std::endl;
   return 0;
}

-1就是PTHREAD_CANCELED宏;

线程退出的3种方式:

  • 从线程函数return。这种方法对主线程不适用,从main函数return相当于调用exit。---推荐
  1. 线程可以调用pthread_ exit终止自己。---推荐
  2. 一个线程可以调用pthread_ cancel终止同一进程中的另一个线程。

5. 多线程

5.1创建多线程

示例:

c++ 复制代码
void* handler_task(void* args)
{
	const char* name = static_cast<char*>(args);
	int cnt = 5;
	while(cnt--)
	{
		sleep(1);
		std::cout<<name<<" is runing ..."<<std::endl;
	}
	return nullptr;
}

const int num = 5;
int main()
{
	//char threadname[64];------结果如图二所示
	std::vector<pthread_t> threads;
    //创建5个线程
	for(int i = 0;i<num;i++)
	{
        //char threadname[64]; ------结果如图一所示
		pthread_t tid;
		char* threadname = new char[64];
		snprintf(threadname,64,"thread-%d",i+1);
		pthread_create(&tid,nullptr,handler_task,threadname);
		threads.push_back(tid);
	}
    //回收线程
	for(auto& tid :threads)
	{
		pthread_join(tid,nullptr);
		std::cout<<" wait thread id: "<<tid<<" success ..."<<std::endl;
	}
	return 0;
}

分析:

图一分析:pthread_create()函数中向线程函数传递参数时,传递不是字符串缓冲区,而是字符数组threadname的地址,每一个线程都拿到时该数组的地址,线程函数中name指针都指向同一地址,每次循环完成后字符串缓冲区就会被释放,而还在主线程还在不断的创建新线程,会向缓冲区内写入其他线程号,当打印时,缓冲区内是当前是几号线程就会被打印出来,第一次写入thread-1,还没有被打印就会被其他线程名覆盖了。

图二分析:虽然,字符串缓冲区没有被释放,但是每个线程name指针都指向同一地址,还是会发生后面线程创建时写入的线程号会覆盖前面线程还没有被打印出来的线程号。

解决方法:给每一个线程创建不同的堆空间,使name指针指向不同的地址,就不会被其他线程影响。

5.2 传参和返回值

示例:

c++ 复制代码
const int num = 5;
//执行任务类
class Task
{
	public:
		Task(int x,int y)
		:_x(x)
		,_y(y)
		{}
		~Task()
		{}
		int Excute()
		{
			return _x+_y;
		}
	private:
		int _x;
		int _y;
};
//线程的信息和要执行的任务
class ThreadData
{
	public:
		ThreadData(int x,int y,std::string threadname)
		:_threadname(threadname)
		,_ptr(x,y)
		{}
		std::string name()
		{
			return _threadname;
		}
		int run()
		{
			return _ptr.Excute();
		}
	private:
	std::string _threadname;
	Task _ptr;
};
//返回结果类
class Result
{
	public:
	Result(int result,std::string threadname)
	:_result(result)
	,_threadname(threadname)
	{}
	~Result()
	{}
	void print()
	{
		std::cout<<_threadname<<" result is : "<<_result<<std::endl;
	}
	private:
	int _result;
	std::string _threadname;
};
void* handlertask(void* args)
{
	ThreadData* data_ptr = static_cast<ThreadData*>(args);
	int result = data_ptr->run(); //获取运行结果
	std::string name = data_ptr->name();//获取线程名
	Result * ret = new Result(result,name);
	delete data_ptr;
	return ret;//返回Result类指针
}
int main()
{
	std::vector<pthread_t> threads;
	//创建线程
	for(int i = 0; i < num; i++)
	{
		pthread_t rid;
		char* threadname = new char[64];
		snprintf(threadname,64,"thread_%d",i+1);//向缓冲区内写入线程号
		ThreadData* data_ptr = new ThreadData(10,20,threadname);
		pthread_create(&rid,nullptr,handlertask,data_ptr);//线程函数参数传递类指针
		threads.push_back(rid);//收集各线程的id信息
	}
	std::vector<Result*> result;
	//线程回收
	void* ret = nullptr;
	for(auto& tid : threads)
	{
		pthread_join(tid,&ret);
		result.push_back(static_cast<Result*>(ret));
	}
	//结果打印
	for(auto& ret: result)
	{
		ret->print();
		delete ret;
	}
	return 0;
}

6. C++11多线程

示例:

makefile 复制代码
Makefile:
版本一:
blog:blog.cc
	g++ -o $@ $^  -std=c++11 
.PHONY:clean
clean:
	rm -rf blog
	
版本二
blog:blog.cc
	g++ -o $@ $^ -lpthread -std=c++11 
.PHONY:clean
clean:
	rm -rf blog
c++ 复制代码
blog.cc
#include<iostream>
#include<thread>
#include<unistd.h>
void handler(int args)
{
	std::cout<<"new thread is runing ...,argu is: "<<args<<std::endl; 
	return;
}
int main()
{
	std::thread t0(handler,10);
	std::thread t1(handler,11);
	std::thread t2(handler,12);
	std::thread t3(handler,13);
	t0.join();
	t1.join();
	t2.join();
	t3.join();
	while(true)
	{
		std::cout<<"main thread is run ..."<<std::endl;
		sleep(1);
	}
}

当使用版本一Makefile编译时会报错:

blog.cc中没有使用pthread_create函数为什么会报没有定义?

原因是:Linux中C++11中的多线程本质是对Linux下的原生线程库的封装,Linux中使用多线程必须使用原生线程库,故需要链接Linux中原生线程库。

为什么C++11中要对多线程进行封装?

提高语言的跨平台性,在Linux中调用Linux中原生线程库,在Windows中调用Windows线程库。

其他语言需要链接吗?

需要,在Linux中只要使用多线程,都会直接或间接使用原生线程库。

7. 线程分离

pthread_join当新线程没有退出是,pthread_join就会阻塞式等待直达新线程退出!!!

示例:

c++ 复制代码
void* newthread(void* args)
{
	int cnt = 3;
	std::string name = static_cast<char*>(args);
	while(cnt--)
	{
		std::cout<<name<<" is run .."<<std::endl;
		sleep(1);
	}
	return nullptr;
}
int main()
{
	pthread_t tid;
	void* ret = nullptr;
	pthread_create(&tid,nullptr,newthread,(void*)"thread-1");
	std::cout<<"main is wait .."<<std::endl;
	int n = pthread_join(tid,&ret);//主线程一直在阻塞式等待newhtreaad跑完,回收newthread资源。
	std::cout<<"main thread wait return ..."<<std::endl;
	return 0;
}

主线程一直在等待,什么都没有做,浪费资源,那如何做到非阻塞式等待呢?

线程分离实现--非阻塞式等待

  • 默认情况下,新创建的线程是joinable的(需要被等待的),线程退出后,需要对其进程pthread_join操作,否则无法释放资源,从而造成系统泄露。
  • 如果不关心线程的返回值,join也是一种负担,我们可以告诉系统,当线程退出时,自动释放线程资源

pthread_detach()

c++ 复制代码
功能: 将某线程设置为分离状态
原型:
	 int pthread_detach(pthread_t thread);
参数:thread:线程ID
返回值:成功返回0;失败返回错误码

可以是线程组内其他线程对目标线程进行分离,也可以是线程自己分离:pthread_detach(pthread_self())

joinable和分离是冲突的,一个线程不能既是joinable又是分离的

示例:

c++ 复制代码
void* newthread(void* args)
{
	int cnt = 2;
	std::string name = static_cast<char*>(args);
	pthread_detach(pthread_self());//方式1:自己将自己设置为分离状态
	while(cnt--)
	{
		std::cout<<name<<" is run .."<<std::endl;
		sleep(1);
	}
	return nullptr;
}
int main()
{
	pthread_t tid;
	void* ret = nullptr;
	pthread_create(&tid,nullptr,newthread,(void*)"thread-1");
	pthread_detach(tid);//方式二:主线程将thread-1设置为分离状态
     //测试二:对已经分离的线程进行等待会返回错误码。
    int n = pthread_join(tid,&ret);
    std::cout<<"main wait return , n : "<<n<<"   "<<strerror(n)<<std::endl;
	int cnt = 4;
	while(cnt--)
	{
		std::cout<<"main thread is run ..."<<std::endl;
		sleep(1);
	}
   
	return 0;
}

测试二:对pthread_join()对已经分离的线程进行等待:错误码是22

如何理解线程分离?

示例:

void* newthread(void* args)
{
	int cnt = 2;
	std::string name = static_cast<char*>(args);
	//pthread_detach(pthread_self());//方式1:自己将自己设置为分离状态
	//新线程永远不退出
	while(true)
	{
		sleep(1);
		std::cout<<name<<" is run .."<<std::endl;
		//----------测试异常------------
		// int * ptr = nullptr;
		// *ptr = 100;
	}
	return nullptr;
}
int main()
{
	pthread_t tid;
	void* ret = nullptr;
	pthread_create(&tid,nullptr,newthread,(void*)"thread-1");
	pthread_detach(tid);
	//主线程3秒后退出
	int cnt = 3;
	while(cnt--)
	{
		sleep(1);
		std::cout<<"main thread is run ..."<<std::endl;
	}
	std::cout<<"main thread quit now ..."<<std::endl;
	return 0;
}

主线程先退出,观察已分离thread-1线程

主线程退出,被分离的thread-1线程也退出了,上一篇进程概念中说过,主线程退出 == 进程退出,进程退出就会释PCB 、进程地址空间、 页表、代码和数据等。虽然thread-1是分离的,但依旧要和主线程共享资源,资源被释放线程也就退出了,也就是说,线程分离,仅仅是不用等待它的执行结果,但是资源还是共享迭代

观察已经分离的thread-1线程出异常时的影响:

分离的thread-1线程出异常时,所有线程都退出。

观察已分离线程的pid:

已分离线程thread-1的pid还是和主进程同属于一个进程中。

总结

如何理解线程分离?

  1. 在底层中,线程依旧属于同一个进程
  2. 只是不需要等待释放资源

8. 理解pthread线程

实验:线程ID是什么东西?

c++ 复制代码
std::string ToHex(int argu)
{
	char buff[64];
	snprintf(buff,sizeof(buff),"0x%x",argu);
	return buff;
}
void* newthread(void* args)
{
	int cnt = 2;
	std::string name = static_cast<char*>(args);
	//pthread_detach(pthread_self());//方式1:自己将自己设置为分离状态
	//新线程永远不退出
	while(true)
	{
		sleep(1);
		std::cout<<name<<" is run ..,pid: "<<getpid()<<std::endl;
	}
	return nullptr;
}
int main()
{
	pthread_t tid;
	void* ret = nullptr;
	pthread_create(&tid,nullptr,newthread,(void*)"thread-1");
	pthread_detach(tid);
	std::cout<<"main thread_id: "<<pthread_self()<<"hex id: "<<ToHex(pthread_self())<<std::endl;
	std::cout<<"new thread_id: "<<tid<<"hex id: "<<ToHex(tid)<<std::endl;
	return 0;
}

两个线程的LWP和线程ID没有关系,那线程ID是什么?

按顺序阅读:

实验:证明线程有独立的栈结构

c++ 复制代码
void* newthread(void* args)
{
	int mark = 10;
	std::string name = static_cast<char*>(args);
	while(true)
	{
		std::cout<<name<<" is run ..,mark: "<<mark<<"&mark: "<<&mark<<std::endl;
		sleep(1);
	}
	return nullptr;
}
int main()
{
	pthread_t tid1;
	pthread_t tid2;
	void* ret = nullptr;
	pthread_create(&tid1,nullptr,newthread,(void*)"thread-1");
	pthread_create(&tid2,nullptr,newthread,(void*)"thread-2");
	pthread_detach(tid1);
	pthread_detach(tid2);
	int cnt = 5;
	while(cnt--)
	{
		sleep(1);
	}
	return 0;
}

两个线程执行newthread但是在各自的栈空间中定义的mark变量,所以两个变量的地址不同。--证明了线程有自己的独立线程栈

实验:线程的局部存储

c++ 复制代码
//验证线程的局部存储
//int g_value = 100; 测试一
__thread int g_value = 100;//测试二
void* newthread1(void* args)
{

	std::string name = static_cast<char*>(args);
	while(true)
	{
		sleep(1);
		std::cout<<name<<" is run ..,g_value: "<<g_value<<"  &g_value: "<<&g_value<<std::endl;
		g_value--;
	}
	return nullptr;
}

void* newthread2(void* args)
{
	std::string name = static_cast<char*>(args);
	while(true)
	{
		sleep(1);
		std::cout<<name<<" is run ..,g_value: "<<g_value<<"  &g_value: "<<&g_value<<std::endl;
	}
	return nullptr;
}
int main()
{
	pthread_t tid1;
	pthread_t tid2;
	void* ret = nullptr;
	pthread_create(&tid1,nullptr,newthread1,(void*)"thread-1");
	pthread_create(&tid2,nullptr,newthread2,(void*)"thread-2");
	pthread_detach(tid1);
	pthread_detach(tid2);
	int cnt = 20;
	while(cnt--)
	{
		sleep(1);
	}
	return 0;
}

测试一结果分析:

现象:两个线程中g_value变量地址相同,同时发生变化。

结论:线程1,线程2,中g_value 属于同一份资源。

测试二结果分析:

现象:全局变量前加__thread,两个线程中变量g_value的地址不同,线程一改变g_value值,线程二没有变化。

结论:__thrad 选项是编译选项,将g_value变量分别拷贝一份给两个线程,存储在两个线程的线程局部存储中,不属于同一份资源,---即线程的局部存储技术。

线程局部存储只能用来存储内置类型,不能存储STL容器

【Linux】线程控制完结,后面篇章还会更深入理解线程,下一篇【Linux】线程互斥

相关推荐
奶香臭豆腐12 分钟前
C++ —— 模板类具体化
开发语言·c++·学习
不想当程序猿_18 分钟前
【蓝桥杯每日一题】分糖果——DFS
c++·算法·蓝桥杯·深度优先
不爱学英文的码字机器19 分钟前
[Linux] Shell 命令及运行原理
linux·运维·服务器
晚夜微雨问海棠呀20 分钟前
长沙景区数据分析项目实现
开发语言·python·信息可视化
graceyun21 分钟前
C语言初阶习题【9】数9的个数
c语言·开发语言
cdut_suye30 分钟前
Linux工具使用指南:从apt管理、gcc编译到makefile构建与gdb调试
java·linux·运维·服务器·c++·人工智能·python
qq_4336184435 分钟前
shell 编程(三)
linux·运维·服务器
波音彬要多做1 小时前
41 stack类与queue类
开发语言·数据结构·c++·学习·算法
捕鲸叉1 小时前
C++软件设计模式之外观(Facade)模式
c++·设计模式·外观模式
Tlzns1 小时前
Linux网络——UDP的运用
linux·网络·udp