在单核中IO密集型适合多线程,CPU密集型不适合
在多核中都适合。
多线程会存在上下文切换,单核CPU密集型等待时间比较少,适合单线程。
- 线程本身的创建和销毁是非常“重”的操作
- 线程本身占用大量内存
- 线程的上下文切换占用大量时间
- 大量线程同时唤醒会使系统经常出现锯齿状负载或者瞬间负载量很大导致宕机
用户空间->内核空间->PCB task_struct -> (页目录,页表,描述地址空间相应的结构(vm_struct, vm_area_struct))--->用户空间--->工作完成进行线程的销毁。
线程的地址空间是共享进程地址空间的,进程有独立的地址空间。但是相应的数据结构都是要创建的
线程池的必要性:线程创建销毁太耗性能
线程数量过多,每个线程都会有一个线程栈空间,会把用户空间占很多,栈空间会被用很多,剩下的空间会不足以完成任务。
线程的调度需要上下文切换,线程数量多的时候,花费的CPU时间很多,会降低CPU利用率。
线程池里线程个数不变,一般是线程池创建时根据CPU核心数量进行指定。
线程池里的线程个数可动态增长。随着task增多,可能会有IO操作阻塞线程,需要动态创建更多的线程来处理task,当任务减少时,当有线程长时间没有处理任务的时候,可以将多的线程销毁(保持初始线程数)
一段代码能不能在多线程环境下运行,要看这段代码是否存在竞态条件,是否在多线程环境下执行,随着线程的调度顺序不同而得到不同结果,称作临界区代码。需要保证它的原子操作。要看代码片段是否可重入。
在进入临界区代码片段时,用锁锁起来,保证临界区代码同一时间只有一个线程在临界区。
ps. CAS操作(无锁机制),可以实现无锁队列等。
同一个进程内的线程共享heap和数据段。只有自己的线程栈是私有的。
count++
mov eax, count
add eax, 1
mov count, eax
一个count++不是原子操作
可以用atomic_int或者atomic原子操作。
没有资源技术限制的mutex互斥锁
std::counting_semaphore
用于计数资源的访问,允许多个线程访问指定数量的资源
std::binary_semaphore
是一个二进制信号量,类似于互斥锁。它只有两个状态:0,1
mutex只能是哪个线程获取的,由哪个线程释放锁
sem.acquire和release可以处在不同线程中调用