线程控制函数
今天学习的都是linux线程库中的函数。<pthread.h>
pthread_creat()创建线程
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
参数
- thread:返回线程ID
- attr:设置线程的属性,attr为NULL表示使用默认属性(默认nullptr
- start_routine:是个函数地址,线程启动后要执行的函数
- arg:传给线程启动函数的参数
返回值
- 成功返回0;失败返回错误码
pthread_exit()线程终止
void pthread_exit(void *value_ptr);
参数:
- value_ptr:value_ptr不要指向一个局部变量。最好指向堆上空间
返回值:
- 没有返回值谢谢。
这个函数类似于线程函数返回,可以说是等同于return。子进程退出正常退出不影响其他线程
但是主线程调用该函数的时候,与return有极大的区别。
主线程return退出
主线程pthread_exit退出
pthread_cancel()线程终止
int pthread_cancel(pthread_t thread)
参数:
- thread:线程ID
返回值:
- 成功返回0;失败返回错误码,成功也没必要返回。
和pthread_exit一样,子终止,主进程不影响,主进程终止,子进程不影响。
配合函数pthread_self()
pthread_t pthread_self(void);
无需传参,哪个线程调用就返回该线程id。
配合使用:pthread_cancel(pthread_self());//中止当前进程。
pthread_join()线程等待
int pthread_join(pthread_t thread, void **value_ptr);
参数:
- thread:线程ID value_ptr:
- 它指向一个指针,后者指向线程的返回值
返回值:成功返回0;失败返回错误码
就是线程结束的时候返回值可以用join的第二个参数保持。
void*function(void*ags)
{
int cnt=0;
int*data=new int[5];
while(1)
{
sleep(1);
data[cnt]=cnt;
cout<<"new thread cnt:"<<cnt++<<endl;
if(cnt==5)
{
break;
}
}
return (void*)data;
}
int main()
{
pthread_t tid;
pthread_create(&tid,nullptr,function,nullptr);
int*data=nullptr;
pthread_join(tid,(void**)&data);
for(int i=0;i<5;i++)
{
cout<<data[i]<<' ';
}
//。。。。。。。。。
}
主进程以阻塞的方式等待编号为tid的线程退出。可以以非阻塞的方式等待退出吗?不可以。
无视线程,对线程分离。
int pthread_detach();线程分离
int pthread_detach(pthread_t thread);
参数:
- 想要分离的线程id
返回值:
- 成功0,失败错误码
可以是线程组内其他线程对目标线程进行分离,也可以是线程自己分离:
pthread_detach(pthread_self());
joinable和分离是冲突的,一个线程不能既是joinable又是分离的。
void*function(void*ags)
{
pthread_detach(pthread_self());
sleep(1);
return nullptr;
}
int main()
{
pthread_t tid;
pthread_t maintid=pthread_self();
pthread_create(&tid,nullptr,function,(void*)maintid);
sleep(2);
int n=pthread_join(tid,nullptr);
cout<<"n:"<<n<<" 错误信息"<<strerror(n)<<endl;
}
分离的线程不可以被主线程等待,但是分离的线程也是整个进程的线程,一旦异常也会对整个进程造成影响。
pthread_kill()对线程发送信号
int pthread_kill(pthread_t thread, int sig);
参数:
- thread:接收信号的线程id
- 发送的信号
返回值:
- 成功返回1,失败返回错误码。
与kill系统函数不同,如果用kill发送信号给同进程的其他线程,那么也只会自己接收到,而pthread_kill则是精准的发送给对应的线程。
对线程id理解
int main()
{
pthread_t tid=pthread_self();
printf("tid:%d 16进制tid:%p \n",tid,tid);
//。。。。。。
}
看起来我们的tid好像一个地址信息。
其实tid就是地址信息,对应的线程其实是操作系统在进程的共享区开辟的一块空间,一份线程一份空间,我们的tid线程id其实是线程空间的起始地址。
对线程栈与局部存储理解
栈
不同的线程在运行期间调用着不同的函数在系统中运行,但是栈区只有一个,每个线程的函数栈帧也不同,一个栈区无法满足多个线程同时运行,所以OS就在地址空间的共享区中,开辟了子线程的自己的栈区,而原先的栈区在多线程与单线程下都是给主线程来运行的。
局部存储
线程也有着自己的局部存储空间,保存这一些私有的数据。
我们定义一个全局变量,然后子线程与主线程同时打印数据与地址,然后子线程修改全局变量,然后继续打印。
int g_val=0;
void*childfunction(void*ags)
{
int cnt=0;
while(1)
{
printf("child thread g_val:%d &g_val:%p cnt:%d\n",g_val,&g_val,++cnt);
if(cnt==5)
{
printf("child change data\n");
g_val=1;
}
sleep(1);
}
return nullptr;
}
int main()
{
pthread_t tid;
pthread_create(&tid,nullptr,childfunction,nullptr);
while(1)
{
printf("main thread g_val:%d &g_val:%p\n",g_val,&g_val);
sleep(1);
}
//........
}
父子线程共享该全局变量,如果不想共享数据,可以在全局变量前加修饰符__thread。这个时候在编译的时候就会在子线程共享区中生成私有的全局变量。
__thread int g_val=0;
确实全局数据被不同的进程访问的是不一样的了。
但是观察发现,似乎主线程的全局数据地址也在共享区,的确主线程也有着自己的线程结构体。
主线程也有对应的,线程数据结构。