目录
一、线程的创建
进程:
- 进程有独立的地址空间
- Linux为每个进程创建task_struct
- 每个进程都参与内核调度,互不影响
线程:
- 进程在切换时系统开销大
- 很多操作系统引入了轻量级进程LWP
- 同一进程中的线程共享相同地址空间
- Linux不区分进程、线程
线程特点:
- 通常线程指的是共享相同地址空间的多个任务
- 使用多线程的好处
- 大大提高了任务切换的效率
- 避免了额外的TLB&cache的刷新
一个进程中的多个线程共享以下资源:
- 可执行的指令
- 静态数据
- 进程中打开的文件描述符
- 当前工作目录
- 用户PID
- 用户组ID
每个线程私有的资源包括:
- 线程ID(TID)
- PC(程序计数器)和相关寄存器
- 堆栈
- 错误号(errno)
- 优先级
- 执行状态和属性
Linux线程库:
- pthread线程库中提供了如下基本操作
- 创建线程
- 回收线程
- 结束线程
- 同步和互斥机制
- 信号量
- 互斥锁
Linux线程不是通过内核实现的,而是通过pthread线程库实现的。
线程创建-pthread_create
#include <pthread.h>
int pthread_create(pthread_t *thread,const
pthread_attr_t *attr,void *(*routine)(void *),void *arg);
成功返回0,失败时返回错误码
thread:线程对象
attr:线程属性,NULL代表默认属性
routine:线程执行的函数
arg:传递给routine的参数,参数是void *,注意传递参数格式
pthread_t pthread_self(void) 查看自己的TID
示例代码:
cpp
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
int *testThread(char *arg)
{
printf("This is a thread\n");
return NULL;
}
int main()
{
pthread_t tid;
int ret;
ret = pthread_create(&tid,NULL,(void *)testThread,NULL); //testThread强制返回void *
printf("This is main thread\n");
sleep(1);
}
编译:
cpp
gcc creatP_t.c -lpthread
结果:
常见错误:
creatP_t.c: In function 'main':
creatP_t.c:14:36: warning: passing argument 3 of 'pthread_create' from incompatible pointer type [-Wincompatible-pointer-types]
ret = pthread_create(&tid,NULL,testThread,NULL);
^
In file included from creatP_t.c:1:0:
/usr/include/pthread.h:233:12: note: expected 'void * (*)(void *)' but argument is of type 'int * (*)(char *)'
解决方法:
ret = pthread_create(&tid,NULL,**(void *)**testThread,NULL); //testThread强制返回void *
注意:
- 主进程的退出,它创建的线程也会推出
- 线程创建需要时间,如果主进程马上退出,那线程不能得到执行
二、线程的参数传递
线程结束-pthread_exit
cpp
#include <pthread.h>
void pthread_exit(void *retval);
- 结束当前线程
- retval可被其他线程通过pthread_join获取
- 线程私有资源被释放
线程查看tid函数
cpp
#include <pthread.h>
pthread_t pthread_self(void);
示例代码:
cpp
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
int *testThread(char *arg)
{
printf("This is a thread, pit = %d, tid = %lu\n",getpid(),pthread_self());
pthread_exit(NULL);
printf("after exit\n");
//return NULL;
}
int main()
{
pthread_t tid;
int ret;
ret = pthread_create(&tid,NULL,(void *)testThread,NULL); //testThread强制返回void *
printf("This is main thread, tid = %lu\n",tid);
sleep(1);
}
运行结果:
获取线程id两种方法:
- 通过pthread_create函数的第一个参数;
- 通过在线程里面调用pthread_self函数
参数传递pthread_create:
cpp
pthread_create(pthread_t *thread,const
pthread_attr_t *attr,void *(*routine)(void *),void *arg);
最后一个参数。
示例代码:
cpp
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void *testThread(void *arg)
{
printf("This is a thread, pit = %d, tid = %lu\n",getpid(),pthread_self());
printf("input arg = %d\n",*(int *)arg);//先将arg参数转换为int *指针类型,再通过*进行解引用
pthread_exit(NULL);
printf("after exit\n");
//return NULL;
}
int main()
{
pthread_t tid;
int ret;
int arg = 5;
ret = pthread_create(&tid,NULL,testThread,(void *)&arg); //testThread强制返回void *
printf("This is main thread, tid = %lu\n",tid);
sleep(1);
}
运行结果:
注意:void *不能直接进行* arg,要强制类型转换为*(int *)arg ,否则会产生如下错误:
值传递代码示例:
cpp
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void *testThread(void *arg)
{
printf("This is a thread, pit = %d, tid = %lu\n",getpid(),pthread_self());
printf("input arg = %d\n",(int)arg);//先将arg参数转换为int *指针类型,再通过*进行解引用
pthread_exit(NULL);
printf("after exit\n");
//return NULL;
}
int main()
{
pthread_t tid;
int ret;
int arg = 5;
ret = pthread_create(&tid,NULL,testThread,(void *)arg); //此时arg表示指针指向的一个值
printf("This is main thread, tid = %lu\n",tid);
sleep(1);
}
编译会出现警告,但是不影响参数传递:
运行结果:
参数传递注意:
- 通过地址传递参数,注意类型的转换
- 值传递,这时候编译器会告警,需要程序员自己保证数据长度正确
创建多个线程代码示例:
cpp
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void *testThread(void *arg)
{
printf("This is a thread, pit = %d, tid = %lu\n",getpid(),pthread_self());
printf("input arg = %d\n",(int)arg);//先将arg参数转换为int *指针类型,再通过*进行解引用
pthread_exit(NULL);
printf("after exit\n");
//return NULL;
}
int main()
{
pthread_t tid[5];
int ret;
int arg = 5;
int i;
for(i = 0; i < 5; i++)
{
ret = pthread_create(&tid[i],NULL,testThread,(void *)i); //testThread强制返回void *
printf("This is main thread, tid = %lu\n",tid[i]);
}
sleep(1);
}
运行结果:
注意:
若出现以下运行错误,
原因:栈被破坏了(数组越界)
三、线程的回收
运行代码实例:
cpp
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void *testThread(void *arg)
{
printf("This is a thread, pit = %d, tid = %lu\n",getpid(),pthread_self());
printf("input arg = %d\n",(int)arg);//先将arg参数转换为int *指针类型,再通过*进行解引用
//pthread_exit(NULL);
while (1)
{
sleep(1);
}
printf("after exit\n");
//return NULL;
}
int main()
{
pthread_t tid[5];
int ret;
int arg = 5;
int i;
for(i = 0; i < 5; i++)
{
ret = pthread_create(&tid[i],NULL,testThread,(void *)i); //testThread强制返回void *
printf("This is main thread, tid = %lu\n",tid[i]);
}
while (1)
{
sleep(1);
}
}
用命令查看进程信息:
用命令查看线程信息:
线程回收-pthread_join:
cpp
#include <pthread.h>
int pthread_join(pthread_t thread,void **retval);
对于一个默认线程的线程A来说,线程占用的资源并不会因为执行结束而得到释放
- 成功返回0,失败返回错误码
- thread要回收的线程对象
- 调用线程阻塞直到thread结束
- *retval接受线程thread的返回值
示例代码:
cpp
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *func(void *arg)
{
printf("This is child thread\n");
sleep(1);
pthread_exit("thread return");
}
int main()
{
void *retv;
pthread_t tid;
pthread_create(&tid,NULL,func,NULL);
pthread_join(tid,&retv);//线程没退出,此处一直阻塞状态
printf("thread ret = %s\n",(char *)retv);
sleep(1);
}
运行结果:
注意:
pthread_join是阻塞函数,如果回收的线程没有结束,则一直等待
线程分离pthread_detach:
cpp
int pthread_detach(pthread_t thread);
成功:0,失败:错误号
- 指定该状态,线程主动与主控线程断开关系。线程结束后(不会产生僵尸线程)
示例代码:
cpp
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *func(void *arg)
{
pthread_detach(pthread_self());
printf("This is child thread\n");
sleep(1);
pthread_exit("thread return");
}
int main()
{
void *retv;
pthread_t tid[5];
int i;
for (i = 0; i < 5; i++)
{
pthread_create(&tid[i],NULL,func,NULL);
//pthread_detach(tid);
}
while (1)
{
sleep(1);
}
}
运行结果:
通过线程属性来设置分离态:
示例代码:
cpp
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *func(void *arg)
{
//pthread_detach(pthread_self());
printf("This is child thread\n");
sleep(1);
pthread_exit("thread return");
}
int main()
{
void *retv;
pthread_t tid[5];
int i;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
for (i = 0; i < 5; i++)
{
pthread_create(&tid[i],NULL,func,NULL);
//pthread_detach(tid);
}
while (1)
{
sleep(1);
}
}
四、线程回收内存演示
示例代码:
cpp
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *func(void *arg)
{
printf("This is child thread\n");
sleep(25);
pthread_exit("thread return");
}
int main()
{
void *retv;
pthread_t tid[100];
int i;
for (i = 0; i < 100; i++)
{
pthread_create(&tid[i],NULL,func,NULL);
}
for (i = 0; i < 100; i++)
{
pthread_join(tid[i],&retv);
printf("thread ret=%s\n",(char *)retv);
}
while (1)
{
sleep(1);
}
}
代码运行期间:
查看进程Pid
使用top命令动态查看内存大小,可以看出内存会变小。
使用pthread_detach代码一样效果。