与进程区别
进程是资源分配的基本单位,线程是cpu执行的基本单位;进程可以看作做是公司管理层,线程则是真正干活的人。
线程可看作是进程的一个执行单元,多个线程共享了同一个进程的地址、文件描述符、文件系统信息等;
同一进程的多线程之间通信开销更小,但也出现了线程同步问题;
同一进程的每个多线程有独立的栈空间,所以线程的创建、销毁、切换比进程消耗资源更少,更适合高并发。
多进程资源隔离比多线程更好。
线程终止
线程本身调用:pthread_exit
其他线程调用:pthread_cancle
pthread_join:等待
线程终止并回收资源;
detached:
主线程不需要等待线程终止,当线程终止后,其资源会自动被系统回收。
线程同步
当多个线程并发访问和修改同一个共享资源(如全局变量)时,如果没有适当的同步
措施,就会导致竞态条件。
可以通过加锁,来避免竞态协议:
互斥锁
同一时刻只有一个线程可执行临界区的代码(类似FreeRtos里的临界区保护)。
初始化:创建互斥锁并初始化。
->
锁定: 获取互斥锁。 如果锁已经被其他线程持有,调用线程将阻塞;
尝试锁定:尝试获取互斥锁。如果锁已被持有, 立即返回而不是阻塞。
->
解锁:释放互斥锁,使其可被其他线程获取。
->
销毁:清理互斥锁资源。
读写锁
读操作:在读写锁的控制下,多个线程可以同时获得读锁。这些线程可以并发地读取
共享资源,但它们的存在阻止了写锁的授予。
写操作:
如果至少有一个读操作持有读锁,写操作就无法获得写锁
(避免线程在读取时资源被修改,导致不同线程读取的内容不同)
。写操作将会阻塞, 直到所有的读锁都被释放。
写饥饿
读操作并发性过高,导致写操作迟迟不能执行。此时可以将策略改为“写优先”来避免。
自旋锁
线程在循环中等待获取,使用于上锁时间很短的资源(避免一直等待浪费资源),主要用于linux内核
信号量
与共享存储等不同,在 Linux 中,信号量是用来协调进程或线程的执行的、
解决多个
进程或线程
间的同步与互斥问题。并不承担传输数据的职责。
互斥(二进制信号量)
确保多个进程或线程
不会同时访问临界区,使
一个线程独占当前资源
。
- 创建互斥量:使用
pthread_mutex_init
函数初始化互斥量。 - 加锁: 使用
pthread_mutex_lock
函数获取(加锁)互斥量。如果互斥量已被其他线程持有,则当前线程会被阻塞,直到互斥量可用。 - 解锁: 使用
pthread_mutex_unlock
函数释放(解锁)互斥量,使其它线程可以获取。
同步(计数信号量)
协调多个进程或线程的执行顺序(排序),确保它们按照一定的
顺序执行
。
在只有两个线程的情况下,计数信号量的使用和FreeRtos中的计数型信号量挺相似的,释放(+1);获取(-1)。可以通过读取信号量值来进行排序。
通常,从编码复杂度和效率的角度考虑,进程间通信用有名信号量,线程间通信用无名信号量。
但如果都要使用进程间通信的话:
有名信号量是
通过
唯一的信号量
名称在操作系统中唯一标识的;
无名信号量是
通过
共享内存
,在进程间共享
内存地址,进而实现通信
。
线程池
线程池(线程的集合)可以有效地处理并发任务,而无需每次都创建和销毁线程(
线程复用
);
这种方法可以减少线程创建和销毁的开销,提高性能和资源利用率;
通过线程池可以很好的控制最多有多少个线程同时运行,如果不使用线程池的话,cpu可能无法同时处理这么多任务(
线程数量控制
);
当有新的任务提交时,线程池会从队列中取出任务分配给空闲线程执行,这样可以平衡任务的提交和执行之间的速度差异,避免任务堆积或者资源浪费(
任务队列);
线程池可以控制线程的执行顺序、优先级和超时等,从而更加灵活地管理系统资源,确保关键任务能够及时得到处理(
资源管理)。
线程池使用时需要链接库。