目录
一、线程的创建
进程:
线程:
线程特点:
一个进程中的多个线程共享以下资源:
每个线程私有的资源包括:
Linux线程库:
线程创建-pthread_create
二、线程的参数传递
线程结束-pthread_exit
线程查看tid函数
三、线程的回收
线程回收-pthread_join:
线程分离pthread_detach:
四、线程回收内存演示
一、线程的创建
进程:
- 进程有独立的地址空间
- 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,constpthread_attr_t *attr,void *(*routine)(void *),void *arg);
成功返回0,失败时返回错误码
thread:线程对象
attr:线程属性,NULL代表默认属性
routine:线程执行的函数
arg:传递给routine的参数,参数是void *,注意传递参数格式
pthread_t pthread_self(void) 查看自己的TID
示例代码:
#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);
}
编译:
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
#include <pthread.h>
void pthread_exit(void *retval);
- 结束当前线程
- retval可被其他线程通过pthread_join获取
- 线程私有资源被释放
线程查看tid函数
#include <pthread.h>
pthread_t pthread_self(void);
示例代码:
#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:
pthread_create(pthread_t *thread,constpthread_attr_t *attr,void *(*routine)(void *),void *arg);
最后一个参数。
示例代码:
#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 ,否则会产生如下错误:
值传递代码示例:
#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);
}
编译会出现警告,但是不影响参数传递:
运行结果:
参数传递注意:
- 通过地址传递参数,注意类型的转换
- 值传递,这时候编译器会告警,需要程序员自己保证数据长度正确
创建多个线程代码示例:
#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);
}
运行结果:
注意:
若出现以下运行错误,
原因:栈被破坏了(数组越界)
三、线程的回收
运行代码实例:
#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:
#include <pthread.h>
int pthread_join(pthread_t thread,void **retval);
对于一个默认线程的线程A来说,线程占用的资源并不会因为执行结束而得到释放
- 成功返回0,失败返回错误码
- thread要回收的线程对象
- 调用线程阻塞直到thread结束
- *retval接受线程thread的返回值
示例代码:
#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:
int pthread_detach(pthread_t thread);
成功:0,失败:错误号
- 指定该状态,线程主动与主控线程断开关系。线程结束后(不会产生僵尸线程)
示例代码:
#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);}}
运行结果:
通过线程属性来设置分离态:
示例代码:
#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);}}
四、线程回收内存演示
示例代码:
#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代码一样效果。