线程基础函数
查看进程中有多少个线程,查看线程的LWP
ps -Lf 进程ID(pid)
执行结果:LWP列
y:~$ ps -Lf 1887
UID PID PPID LWP C NLWP STIME TTY STAT TIME CMD
ys 1887 1341 1887 0 3 14:57 tty2 Sl 0:00 /usr/lib/ibus/ibus
ys 1887 1341 1889 0 3 14:57 tty2 Sl 0:00 /usr/lib/ibus/ibus
ys 1887 1341 1890 0 3 14:57 tty2 Sl 0:00 /usr/lib/ibus/ibus
线程共享的资源:
注意:信号和线程最好不要一起使用。又用信号又用多线程的架构不太合理。
- 文件描述符表
- 信号的处理方式
- 当前工作目录
- 用户ID和组ID
- 内存地址空间(.text/.data/.bss/heap/共享库,栈区(stack)不共享)
非线程共享的资源:
线程ID
处理器现场和栈指针(内核栈)
独立的栈空间(用户空间栈)
errno变量
所以不能用函数perrno打印错误信息了。
使用strerror函数打印错误信息
#include <string.h> char *strerror(int errnum);
- errnum:errno
阻塞信号集合
调度优先级
现场优,缺点
- 优点:1,提高程序并发性。2,开销小。3,数据通信,共享方便
- 缺点:1,因为使用的时库函数,所以不太稳定。2,调试,编写困难。3,对信号支持不好。
进程和线程都是通过系统函数clone创建的。
每个线程有自己独立的PCB
pcb:进程控制块结构体:/usr/src/linux-headers-4.15.0-29/include/linux/sched.h
进程id:系统中每个进程有唯一的id,在c语言中用pid_t类型表示,是个非负整数。
进程状态:就绪,运行,挂起,停止等状态
描述虚拟地址空间的信息
描述控制终端的信息
进程执行时的当前工作目录(current working directory)
umask掩码
文件描述符表,包含很多指向file结构体的指针
和信号相关的信息
用户id和组id
会话(session)和进程组
进程可以使用的资源上限(Resource Limit)
用【ulimit -a】查看:
ys@ys:~$ ulimit -a core file size (blocks, -c) 0 data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 7743 max locked memory (kbytes, -l) 16384 max memory size (kbytes, -m) unlimited open files (-n) 1024 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 8192 cpu time (seconds, -t) unlimited max user processes (-u) 7743 virtual memory (kbytes, -v) unlimited file locks (-x) unlimited
pthread_create函数:创建一个线程,并开始执行这个线程
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
- thread:线程的ID
- attr:线程的属性,一般不使用
- start_routine:函数指针
- arg:start_routine函数的参数
- 返回值:成功返回0;失败返回errno
编译的时候要加【-lpthread】
pthread_self函数:得到线程的id
#include <pthread.h>
pthread_t pthread_self(void);
例子:得到主线程的ID和子线程的ID。注意要sleep1秒,不睡的话,主线程就先结束了,所以子线程里的打印打不出来。
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
void * thr(void* args){
printf("in thread %d, %lu\n", getpid(), pthread_self());
}
int main(){
pthread_t tid;
pthread_create(&tid, NULL, thr, NULL);
printf("in main thread %d, %lu\n", getpid(), pthread_self());
sleep(1);
return 0;
}
pthread_exit函数:终止线程
#include <pthread.h>
void pthread_exit(void *retval);
- retval:线程的返回值
改进上面的例子,用pthread_exit代替sleep
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
void * thr(void* args){
printf("in thread %d, %lu\n", getpid(), pthread_self());
//exit(1);//不要在子线程里调用此函数,调用的结果是把整个进程终止了。
//return NULL;//可以使用
//pthread_exit(NULL);//可以使用
}
int main(){
pthread_t tid;
pthread_create(&tid, NULL, thr, NULL);
printf("in main thread %d, %lu\n", getpid(), pthread_self());
//sleep(1);
pthread_exit(NULL);
return 0;
}
终止线程时的注意事项
- 要使用pthread_exit函数,不能使用exit函数
- 也可以使用return,但是在主线程里使用return的话,就把进程终止了。
- exit是推出进程,所以不要在线程函数里调用exit函数
pthread_join函数:阻塞等待回收线程资源,其实也是等待线程结束,所以是阻塞的函数,线程不执行完,pthread_join就一直处于阻塞状态,类似wait函数(回收子进程的函数,也是阻塞的)。并且它的第二个参数是可以接收线程的返回值的。
线程不回收也会变成僵尸线程,线程里也有PCB资源,也要回收。
#include <pthread.h>
int pthread_j