一、线程概念
-
基础
线程又称LWP:light weight process 轻量级的进程,(在linux环境下)本质仍是进程。 进程:独立地址空间,拥有PCB 线程:有独立的PCB,但没有独立的地址空间(共享) 区别: 在于是否共享地址空间。 独居(进程);合租(线程) Linux下: 线程:最小的执行单位 进程:最小分配资源单位,可看成是只有一个线程的进程 GDB和信号诞生时间比线程早,故配合使用会很麻烦。 线程越多的程序理论上越可能争夺到cpu资源,但不是一味的扩展线程数,会有峰值。 ps -Lf 进程id //查看线程运行情况 包括线程号(cpu执行的最小单位,不等同于线程id)
-
linux内核线程实现原理(了解即可)
1.轻量级进程(lwp),也有PCB,创建线程使用的底层函数和进程一样,都是clone。 2.从内核里看进程和线程是一样的,都有各自不同的PCB,但是PCB中指向内存大额三级页表是相同的(详见下面三级映射)。 3.进程可以锐变成线程 4.线程可看做寄存器和栈的集合 5.linux下,线程是最小执行单位,进程是最小分配资源单位 三级映射: 进程PCB->页目录(可看成数组,首地址位与PCB中)->页表->物理页面->内存单元。 PCB中由三级映射出物理地址。 PCB中描述虚拟地址的指针指向一个【页面】(页面中都是指针), 这个页面中的指针再指向一个【页表】(页表中也都是指针), 页表再对应一个【页目录】(页目录中都是内存单元(对应物理内存))。 多进程: 每个进程对应不同的PCB,且有不同的页面指针。故不共享数据。 多线程: 每个线程对应不同的PCB,但有相同的页面指针。故共享数据。
-
线程共享资源
1.文件描述符表 2.每种信号的处理方式 (线程和信号尽可能不混用) 3.当前工作目录 4.用户ID 和 组ID 5.内存地址空间(.text/.data/.bss/heap/共享库,栈不共享)
-
线程非共享资源
1.线程ID 2.处理器现场(寄存器相关)和栈指针(内核栈) 3.独立的栈空间(用户空间栈) 4.errno 变量 5.信号屏蔽字 6.调度优先级
-
线程优、缺点
二、线程控制原语(相关函数)
线程相关函数出错都返回errno,
故利用char * strerror(errno)函数来打印出错误信息。
而不用perror()。
-
pthread_self
man 3 pthread_self 1)NAME pthread_self - obtain ID of the calling thread 2)SYNOPSIS #include <pthread.h> pthread_t pthread_self(void); Compile and link with -pthread. //特别注意 3)RETURN VALUE This function always succeeds, returning the calling thread's ID.
-
pthread_create
man 3 pthread_create 1)NAME pthread_create - create a new thread 2)SYNOPSIS #include <pthread.h> int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); Compile and link with -pthread. //特别注意 3)PARAMETER (1)thread:值结果参数,线程ID。 (2)attr:设置线程属性。NULL表示使用默认属性(共两种属性)PTHREAD_CREATE_JOINABLE,可配合pthread_join()使用; 另一种属性为PTHREAD_CREATE_DETACHED,不能配合pthread_join()使用,会报错。详见下 7. (3)start_routine:子线程回调函数。若线程创建成功,该函数自动被调用。 (4)arg:start_routine指定函数的参数。没有的话传NULL。 传参时一定要注意,不要传地址(数据共享原因),要传值进去,否则当回调函数得到数据时会出错。 4)RETURN VALUE On success, pthread_create() returns 0; on error, it returns an error number, and the contents of *thread are undefined. DEMO: https://github.com/Panor520/LinuxCode/tree/master/thread/pthread.c 循环创建多个线程DEMO: https://github.com/Panor520/LinuxCode/tree/master/thread/pthread_muti.c
-
pthread_exit
exit(0); //退出当前进程 return 0;//返回到调用者那里去。 //main函数中的return就代表进程结束。 pthread_exit(0); //退出当前线程 man 3 pthread_exit 1.NAME pthread_exit - terminate calling thread 2.SYNOPSIS #include <pthread.h> void pthread_exit(void *retval); Compile and link with -pthread. 3.PARAMETER retval:返回值。类似于exit(0);中的0. 4.RETURN VALUE This function does not return to the caller. DEMO: https://github.com/Panor520/LinuxCode/tree/master/thread/pthread_muti.c
-
pthread_join
man 3 pthread_join 1.NAME pthread_join - join with a terminated thread 2.SYNOPSIS #include <pthread.h> int pthread_join(pthread_t thread, void **retval); Compile and link with -pthread. 3.PARAMETER thread:线程ID retval:值结果参数,得到pthread_create指定的线程回调函数的结果。 为什么是(viod**)的返回类型。因为回调函数是void *,而再回调void* 就是void**. 类似的wait回收int型数据为 int ——> int * 则有 void* ——> void ** 注意: 当线程被pthread_cancel()后,retval的值是-1,代表线程非正常消亡 4.RETURN VALUE On success, pthread_join() returns 0; on error, it returns an error number. DEMO: https://github.com/Panor520/LinuxCode/tree/master/thread/pthread_join.c
-
pthread_cancel
man 3 pthread_cancel 1.NAME pthread_cancel - send a cancellation request to a thread 2.SYNOPSIS #include <pthread.h> int pthread_cancel(pthread_t thread); Compile and link with -pthread. 3.PARAMETER thread:指定要取消的进程ID 4.RETURN VALUE On success, pthread_cancel() returns 0; on error, it returns a nonzero error number. 5.注意 只有程序运行到取消点后(取消点函数执行完毕)才会取消,如果线程运行中(指回调函数)没有取消点,那就一直执行,不会取消。 当程序中没有取消点时,可自行增加取消点,利用函数pthread_testcancel()。 //man pthread_testcancel 取消点( man pthreads 中可查看): 一些和内核进行交互的函数,如sleep()、close()等等常用的函数. DEMO: https://github.com/Panor520/LinuxCode/tree/master/thread/pthread_cancel.c
-
pthread_detach
man 3 pthread_detach 创建完线程后再指定线程分离,线程执行完自行回收,不能和pthread_join同时使用。 1.NAME pthread_detach - detach a thread 2.SYNOPSIS #include <pthread.h> int pthread_detach(pthread_t thread); Compile and link with -pthread. 3.RETURN VALUE On success, pthread_detach() returns 0; on error, it returns an error number. Demo: https://github.com/Panor520/LinuxCode/tree/master/thread/pthread_detach.c
-
pthread_create 参数2设置线程属性
简单步骤: pthread_attr_t attr;//创建线程属性结构体 pthread_attr_init(&attr);//初始化结构体 pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);//设置线程属性结构体为 detach 状态 pthread_create(&tid,&attr,tfunc,NULL);//利用线程属性结构体创建线程 pthread_attr_destroy(&attr);//在利用完线程属性结构体后销毁该结构体 函数详解: (1)pthread_attr_init()、pthread_attr_destroy() man 3 pthread_attr_init 1.NAME pthread_attr_init, pthread_attr_destroy - initialize and destroy thread attributes object 2.SYNOPSIS #include <pthread.h> int pthread_attr_init(pthread_attr_t *attr); //类似malloc() int pthread_attr_destroy(pthread_attr_t *attr); //类似free() Compile and link with -pthread. 3.RETURN VALUE On success, these functions return 0; on error, they return a nonzero error number. (2)pthread_attr_setdetachstate()、pthread_attr_getdetachstate() 1.NAME pthread_attr_setdetachstate, pthread_attr_getdetachstate - set/get detach state attribute in thread attributes object 2.SYNOPSIS #include <pthread.h> int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate); Compile and link with -pthread. 3.PARAMETER 1)attr 2)detachstate: PTHREAD_CREATE_DETACHED Threads that are created using attr will be created in a detached state. PTHREAD_CREATE_JOINABLE Threads that are created using attr will be created in a joinable state. attention: The default setting of the detach state attribute in a newly initialized thread attributes object is PTHREAD_CREATE_JOIN‐ ABLE. 4.RETURN VALUE On success, these functions return 0; on error, they return a nonzero error number. DEMO: https://github.com/Panor520/LinuxCode/tree/master/thread/pthread_attr_init.c
-
线程使用相关注意事项
1.主线程退出其他线程不退出,主线程应调用pthread_exit。 2.避免僵尸进程有两种方法: 1)pthread_join //线程创建默认属性 2)pthread_detach //需指定设置,两种设置方法 1.pthread_create()时利用线程属性指定 2.pthread_create()后指定。 3.malloc和mmap申请的内存可以被其他线程释放。 4.应避免在多线程中调用fork(),子进程中只有调用fork的线程存在,其他线程在子进程均pthread_exit(). 5.应避免在多线程中引入信号机制。 理由:每个线程有独立的PCB,故mask(屏蔽字集合)不同,但每个线程对信号的处理是共享的(因为在一个进程下), 而由哪个线程来获取信号是靠CPU分配的,而当一个线程处理完之后,其他线程是不受影响的。
三、线程和进程控制原语比较
四、线程同步概念
官方释义:
线程同步,指一个线程发出某一功能调用时,在没有得到结果之前,该调用不放回。同时其它线程为保证数据一致性,不能调用该功能。
我的理解:
就是保证不同线程方法调用共享数据时,要保证上一个线程执行完毕后,下一个线程再执行。保证共享数据一致性。
同步的目的:
避免数据混乱。解决与时间有关的错误。
实际上不仅线程间需要同步,进程间、信号间等等都需要同步机制。
数据混乱原因:
1.资源共享
2.调度随机
3.线程间缺乏必要的同步机制。#下面主要就把讲这个。
五、线程同步——互斥锁(互斥量)
-
概念
Linux中提供一把互斥锁mutex(也称为互斥量)。 每个线程在对资源操作前都尝试加锁(没拿到锁就一直阻塞等待锁可用),成功加锁才能操作,操作结束后解锁。 资源还是共享的,线程间也还是竞争的。 但通过“锁”将资源访问变成互斥操作,但注意同一时间只能有一个线程持有该锁。 建议锁,没有强制性。 所有线程在访问公共数据前先拿锁再访问。但锁本身不具备强制性。
-
互斥锁实现
使用互斥锁时尽可能访问共享数据前加锁,访问后立即解锁。 互斥锁本质是结构体。可以看成整数。初值(pthread_mutex_init()后)为1. 加锁: -- 阻塞线程。 解锁: ++ 唤醒阻塞线程。 实现步骤: pthread_mutex_t mylock;//定义互斥锁结构体,应声明为全局变量,放在全局位置 pthread_mutex_init(&mylock,NULL);//初始化互斥锁,第二个参数默认NULL即可 pthread_mutex_lock(&mylock);//加锁,线程阻塞,等待锁释放 pthread_mutex_trylock(&mylock)//加锁,线程不阻塞,立即返回,线程不会阻塞 pthread_mutex_unlock(&mylock);//解锁 pthread_mutex_destroy(&mylock);//销毁锁 1.pthread_mutex_t mutex锁结构体 2.pthread_mutex_init()、pthread_mutex_destroy() man pthread_mutex_init (1)NAME pthread_mutex_destroy, pthread_mutex_init — destroy and initialize a mutex (2)SYNOPSIS #include <pthread.h> int pthread_mutex_destroy(pthread_mutex_t *mutex); int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr); pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; (3)PARAMETER 1.mutex:互斥锁结构体 restrict关键字:只能引用定义的指针,不能使用另外赋值的指针。 例:int *p=0xdddd; int * q=p; 当定义函数int test(int * restrict x);时,参数x知道能传p,不能穿q。 2.attr 填NULL即可。 (4)RETURN VALUE If successful, the pthread_mutex_destroy() and pthread_mutex_init() functions shall return zero; otherwise, an error num‐ ber shall be returned to indicate the error. 3.pthread_mutex_lock()、pthread_mutex_unlock() man pthread_mutex_lock (1)NAME pthread_mutex_lock, pthread_mutex_trylock, pthread_mutex_unlock — lock and unlock a mutex (2)SYNOPSIS #include <pthread.h> int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_trylock(pthread_mutex_t *mutex); int pthread_mutex_unlock(pthread_mutex_t *mutex); (3)RETURN VALUE If successful, the pthread_mutex_lock(), pthread_mutex_trylock(), and pthread_mutex_unlock() functions shall return zero; otherwise, an error number shall be returned to indicate the error. DEMO: https://github.com/Panor520/LinuxCode/tree/master/thread/pthread_mutex.c
六、死锁
两种情况:
1.线程试图对同一个互斥量A加锁两次
2.线程1 拥有 A锁,请求获取 B锁;线程2 拥有 B锁,请求获得A锁。
DEMO:
七、读写锁
-
概念
锁只有一把: 以读方式给数据加锁——读锁。 以写方式给数据加锁——写锁。 读共享,写独占。 写锁优先级高。 【相较于互斥量而言,当读线程多的时候,提高访问效率】。 特性如下图:
-
读写锁实现
实现步骤: pthread_rwlock_t mylock;//定义读写锁全局变量 pthread_rwlock_init(&mylock,NULL);//动态初始化读写锁 pthread_rwlock_t mylock = PTHREAD_MUTEX_INITIALIZER pthread_rwlock_rdlock(&mylock);//加读锁,阻塞线程直到锁可用 pthread_rwlock_tryrdlock(&mylock);//加读锁,不阻塞线程,直接返回结果 pthread_rwlock_wrlock(&mylock);//加写锁,阻塞线线程直到锁可用 pthread_rwlock_trywrlock(&mylock);//加写锁,不阻塞线程,直接返回结果 pthread_rwlock_unlock(&mylock);//解锁 pthread_rwlock_destroy(&mylock);//销毁读写锁 函数解析: 1.pthread_rwlock_init()、pthread_rwlock_destroy() man pthread_rwlock_init (1)NAME pthread_rwlock_destroy, pthread_rwlock_init — destroy and initialize a read-write lock object (2)SYNOPSIS #include <pthread.h> int pthread_rwlock_destroy(pthread_rwlock_t *rwlock); int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr); pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; (3)PARAMETER 1.rwlock:互斥锁结构体 restrict关键字:只能引用定义的指针,不能使用另外赋值的指针。 例:int *p=0xdddd; int * q=p; 当定义函数int test(int * restrict x);时,参数x知道能传p,不能穿q。 2.attr 填NULL即可。 (4)RETURN VALUE If successful, the pthread_rwlock_destroy() and pthread_rwlock_init() functions shall return zero; otherwise, an error number shall be returned to indicate the error. 2.pthread_rwlock_rdlock(), pthread_rwlock_tryrdlock() man pthread_rwlock_rdlock (1)NAME pthread_rwlock_rdlock, pthread_rwlock_tryrdlock — lock a read-write lock object for reading (2)SYNOPSIS #include <pthread.h> int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); (3)RETURN VALUE 成功:0 失败:错误号。errno。 3.pthread_rwlock_wrlock(),pthread_rwlock_trywrlock() (1)NAME pthread_rwlock_trywrlock, pthread_rwlock_wrlock — lock a read-write lock object for writing (2)SYNOPSIS #include <pthread.h> int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); (3)RETURN VALUE 成功:0 失败:错误号。errno。 4.pthread_rwlock_unlock() (1)NAME pthread_rwlock_unlock — unlock a read-write lock object (2)SYNOPSIS #include <pthread.h> int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); (3)RETURN VALUE If successful, the pthread_rwlock_unlock() function shall return zero; otherwise, an error number shall be returned to indicate the error. DEMO: https://github.com/Panor520/LinuxCode/tree/master/thread/pthread_rwlock.c
八、条件变量
-
条件变量概念
条件变量本身不是锁,但可以造成线程阻塞。 通常与互斥锁配合使用(例如下面的生产者消费者模型)。 主要函数如下:
man 手册找不到下列函数。就用下面的安装命令安装。 sudo apt-get install manpages-posix-dev 1. pthread_cond_t pthread_cond_t cond;//条件变量结构体 初始化条件变量: pthread_cond_init(&cond,NULL); //动态初始化 pthread_cond_t cond = PTHREAD_COND_INITIALIZER; //静态初始化 2. pthread_cond_init()、pthread_cond_destroy() 1)NAME pthread_cond_destroy, pthread_cond_init — destroy and initialize condition variables 2)SYNOPSIS #include <pthread.h> int pthread_cond_destroy(pthread_cond_t *cond); int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr); pthread_cond_t cond = PTHREAD_COND_INITIALIZER; 3)PARAMETER cond :条件变量结构体 attr :NULL即可。 4)RETURN VALUE If successful, the pthread_cond_destroy() and pthread_cond_init() functions shall return zero; otherwise, an error number shall be returned to indicate the error. 3.pthread_cond_timedwait(), pthread_cond_wait() 1)NAME pthread_cond_timedwait, pthread_cond_wait — wait on a condition 2)SYNOPSIS #include <pthread.h> int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime); int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex); 3)PARAMETER cond :互斥量结构体 mutex :初始化过的互斥锁结构体 4)RETURN VALUE Upon successful completion, a value of zero shall be returned; otherwise, an error number shall be returned to indicate the error. 4. pthread_cond_broadcast(),//唤醒阻塞在条件变量上的 所有线程 pthread_cond_signal() //唤醒阻塞在条件变量上的 (至少)一个线程 1)NAME pthread_cond_broadcast, pthread_cond_signal — broadcast or signal a condition 2)SYNOPSIS #include <pthread.h> int pthread_cond_broadcast(pthread_cond_t *cond); int pthread_cond_signal(pthread_cond_t *cond); 3)PARAMETER cond:条件变量结构体 4)RETURN VALUE If successful, the pthread_cond_broadcast() and pthread_cond_signal() functions shall return zero; otherwise, an error number shall be returned to indicate the error. DEMO: 详见 八、生产者与消费者
-
wait和timewait详解
九、生产者消费者模型(条件变量实现)
-
基础
-
DEMO
十、信号量
-
基础
信号量和互斥锁(1)类似,只是互斥锁最多只允许一个线程访问共享数据,而信号量(N)可以是多个线程访问。 sem_t sem;//定义类型 sem_wait(); //一次调用,做一次-- 操作,当信号量的值为 0 时,再次 -- 会阻塞。(对比 pthread_mutex_lock) sem_post();//一次调用,做一次++ 操作,当信号量的值为 N 时,再次 ++ 就会阻塞。(对比pthread_mutex_unlock)
-
涉及函数
1.sem_t sem;//定义类型 2.sem_init() man sem_init 1)NAME sem_init - initialize an unnamed semaphore 2)SYNOPSIS #include <semaphore.h> int sem_init(sem_t *sem, int pshared, unsigned int value); Link with -pthread. 3)PARAMETER sem:信号量 pshared :0 线程同步 1 进程同步 value :N值。信号量值。 (指定同时访问的线程数) 4)RETURN VALUE sem_init() returns 0 on success; on error, -1 is returned, and errno is set to indicate the error. 3.sem_destroy() 1)NAME sem_destroy - destroy an unnamed semaphore 2)SYNOPSIS #include <semaphore.h> int sem_destroy(sem_t *sem); Link with -pthread. 3)RETURN VALUE sem_destroy() returns 0 on success; on error, -1 is returned, and errno is set to indicate the error. 4.sem_wait(),sem_timedwait() //(对比 pthread_mutex_lock) man sem_wait 1)NAME sem_wait, sem_timedwait, sem_trywait - lock a semaphore 2)SYNOPSIS #include <semaphore.h> int sem_wait(sem_t *sem); int sem_trywait(sem_t *sem); int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); Link with -pthread. 3)PARAMETER abs_timeout :采用的是绝对时间。和普通的定时不同。 例:定时1秒 time_t cur = time(NULL);//获取当前时间 struct timespect t;//定义时间结构体变量 t.tv_sec = cur+1;//定时1s. t.tv_nsec = t.tv_sec +100; sem_timewait(&sem,&t);//应用传参 4)RETURN VALUE All of these functions return 0 on success; on error, the value of the semaphore is left unchanged, -1 is returned, and errno is set to indicate the error. 5.sem_post() //(对比pthread_mutex_unlock) man sem_post 1)NAME sem_post - unlock a semaphore 2)SYNOPSIS #include <semaphore.h> int sem_post(sem_t *sem); Link with -pthread. 3)RETURN VALUE sem_post() returns 0 on success; on error, the value of the semaphore is left unchanged, -1 is returned, and errno is set to indicate the error.
-
利用信号量实现生产者消费者模型
DEMO: https://github.com/Panor520/LinuxCode/tree/master/thread/sem_produce_consumer.c DEMO配合下图看。
十一、线程池
-
基础概念
epoll 实现 client 端和 server 端的连接。 线程池 实现 server 端高效处理和客户端连接的操作。 线程池的实现利用 条件变量 和 锁 机制。
-
实现
DEMO还没来的及写,自行百度