线程和进程
以前我们要同时跑多个程序,可以通过fork()多个子进程,然后通过系统函数进行程序的替换,但是创建进程代价大,不仅要拷贝一份父进程的地址空间,页表,文件表述符表等。但是线程不需要因为是进程的执行流,共享同个地址空间,页表,只需让不同线程执行不同的代码块(函数就可以了)。
一、线程函数接口
它们的返回值都比较统一,成功就返回0,失败就返回错误码
(1)线程创建
第一个参数类型是我们在定义的一个pthread_t类型的变量指针,通过它我们可以拿到 用户识别的线程id。第二个参数设置为空。第三个参数是函数指针,第四个是我们要传入线程执行函数的参数,由于它的类型是void*,我们可以传入任意类型
(2)线程等待
线程和进程一样,虽然是一个进程的地址空间的执行流,但是也要进行等待回收,不然会造成类似内存泄漏 问题。retval是输出型参数,通过它可以拿到线程退出信息(简单说就是线程执行函数的返回值)。
(3)线程中止
进程有退出码,线程没有,只有我们自己写的返回值,既用pthread_exit()返回,或者直接return返回自定义的码(由于返回值类型是void*,要强转),不建议用exit()因为会造成主线程退出,主线程退出了,进程资源就释放了,所有线程就跟着退出了。通常这返回值信息会被线程等待函数pthread_join()拿到。
(4)线程分离
以前子进程退出如果父进程不进行等待,我们可以自定义捕捉函数对子进程发出的退出信号进行忽略,不会有僵尸进程。线程也可以通过分离,让主线程不用主动对它进行等待,就算线程退出也不会有类型内存泄漏问题。注意的是,线程分离只是一种工作状态,它和没分离的线程几乎一样,只是不用等待了。
二,多线程的创建
pthread_create函数参数由于是void*,我们就可以传任意类型的对象
makefile
test:classpthreads.cc
g++ -o $@ $^ -std=c++11 -lpthread
.PHONY:clean
clean:
rm -f test
classpthreads.cc
#include<iostream>
using namespace std;
#include<pthread.h>
#include<unistd.h>
#include<string.h>
#include <sys/types.h>
#include<vector>
namespace ljh{
class Task{
public:
Task():datex(0),datey(0)
{
}
void SetDate(int x,int y)
{
datex=x;
datey=y;
}
int Excute()
{
return datex+datey;
}
~Task()
{}
private:
int datex;
int datey;
};
class threaddate:public Task
{
public:
threaddate(int x,int y,char* threadname )
:_x(x),_y(y),_threadname(threadname)
{
s.SetDate(_x,_y);
}
string getname(){
return _threadname;
}
int run()
{
s.Excute();
}
private:
string _threadname;
int _x;
int _y;
Task s;
};
class Result{
public:
void SetResult(int result,string& threadname)
{
_result=result;
_threadname=threadname;
}
void Print()
{
cout<<"result:"<<_result<<"threadname"<<_threadname<<endl;
}
private:
int _result;
string _threadname;
};
}
using namespace ljh;
void* handlerTask(void*p)
{
threaddate* td=static_cast<threaddate*>(p);
string name=td->getname();
Result* result=new Result();
int ret=td->run();
result->SetResult(ret,name);
delete td;
sleep(2);
return result;
}
vector<Result*> ret;
vector<pthread_t> pthreadname;
int main()
{
for(int i=0;i<5;i++)//创建5个线程
{
char* name=new char[64];
pthread_t id;
snprintf(name,sizeof(name),"Thread_%d",i+1);
threaddate* p=new threaddate(2,6,name);
pthread_create(&id,nullptr,handlerTask,p);
pthreadname.push_back(id);
}
for(auto e:pthreadname)
{
void* s=nullptr;//返回值,void*
pthread_join(e,&s);//线程等待回收
ret.push_back((Result*)s);
}
return 0;
}
三.创建的线程和主线程之间关系
1.多线程只是主线程的执行流,主线程main退出,子进程也会退出,所以我们必须让主线程最后退出
2.创建的新线程和主线程,哪个先运行,这个取决与调度器。
线程共享和私有
(1)共享:代码和全局数据和进程文件描述符表
因为它们拥有同一块地址空间
(2)私有:线程的硬件上下文数据(cpu寄存器的值),线程的独立栈结构。对于多进程来说,线程的上下文数据比进程少,所以也叫线程为轻量级进程。
我们可以用命令查看(ps -aL | grep xxx).对于栈来说,不同线程可以分为进程地址空间的栈空间还有线程独立的栈,访问全局数据就时访问进程地址空间主栈,在线程执行函数里面变量之类的就是线程独立的栈。
验证:创建3个线程,定义一个全局变量vale,还有线程执行函数的n,不同的线程打印全局vale地址是相同,n的地址却是不同的。