楼主本来是要继续写服务器并发的,但是后续的服务器相关点都和进程线程联系在一起,所以先把进程线程相关内容写完吧!

这次只写进程线程的概述,实际操作后续博文逐一代码实现。

 

进程同步or进程通信/线程同步or线程通信?

这两组概念迷惑我至今,网上和书籍对这个的描述也是爱用啥用啥的感觉,今天又重新理了一遍。

 

什么是同步:同步就是数据保持一致,无论是进程还是线程,都是实现了代码执行流程的分支,多个分支同时进行。多个分支互不干扰,但是又有些数据需要共享,让这些数据对所有分支保持一致即为同步。

什么是通信:通信就是数据传输,数据存在两块不同的内存区域。通过某种方式互相传递。

但是在进程线程中,比如面试官问你进程同步有那些方式,管道算是同步还是通信?干脆也懒得区分,还是按传统习惯,同步,通信一并处理吧,理解成同一个玩意。

 

进程:

linix一个程序就是一个进程,想产生新的进程只有唯一的一个方法:fork(),这里不讨论开启或调用其他程序场景。

进程完全拷贝资源,两个进程完全独立,从fork()那段代码开始,复制执行完毕的代码内存,然后分道扬镳各自独立执行,进程的数据不是共享的,同一变量占用的内存地址不一样。

虽然完全一致,还是有父子区别的,fork()返回两次,返回值为0的是子进程,返回子进程pid的是父进程。

 

使用进程要注意什么:

  1. 父进程不回收子进程会产生僵尸进程,即子结束了父还在执行并且没有回收导致子进程依然占用内核资源,解决办法为捕获子进程结束信号,执行waitpid回收子进程。
  2. 进程最好在代码较干净时产生,在多线程下产生进程要重置锁状态,如本进程拷贝锁状态,产生进程后用户认为是空闲的,但实际其他进程占用锁,可能导致获取锁阻塞。
  3. fork()拷贝代码所有资源,但是不拷贝线程。在线程下执行fork(),不会拷贝其他线程,只拷贝本进程,所以才会产生2描述的死锁。
  4. 使用临界资源时需要获得信号量,保证临界资源的唯一访问。

进程同步方式:

  1. 管道,只局限与父子进程。
  2. 信号,进程间传递信号,捕获到信号后执行对应绑定的代码,和QT的信号槽类似。可以实现进程通信的“单播”、“广播”。
  3. 信号量,信号量本身无法传递数据,配合共享内存使用,类似于线程中的锁,用于保护临界资源。
  4. 共享内存,进程间最常用的数据同步方式。与信号量配合使用。
  5. 消息队列,也是非常常见的同步方式,把数据放入队列,内核逐一处理发送至目的线程。
  6. socket ? 网上很多提及到这种方式,但是《Unix网络编程》、《Linux高性能服务器编程》及自己工作中都没见过这种方式,有消息队列为何还要用socket?应用层一直到链路层,效率应该不怎么样吧,对网上这提出的socket保持怀疑态度,主机间还是可以理解的。

线程:

linux线程直到1996年才出现,Linux线程分LinuxThread和NPTL两个版本,可使用getconf GNU_LIBPTHREAD_VERSION 查看。

前者实际上是进程的衍生版的轻量级进程,效率较低,占用资源多,现在已经被抛弃的差不多了。后者是真正意义上的线程,不会产生进程。

线程里数据是共享的,即同一变量占用同一个内存地址,所以用全局变量就可以轻松实现数据交流。

 

使用线程要注意什么:

  1. 线程创建后处于join态,结束时类似进程,需要进行回收:pthread_join()。也可以创建后将其join态置为脱离态,结束自动销毁。
  2. 线程访问临界资源需要信号量或互斥锁控制,有时还需要控制变量控制线程先后。
  3. 线程分抢占式和非抢占式,抢占式即每个线程轮流占用一段时间,这个时间是2毫秒,非抢占式按优先级轮流执行,时间不限。这个即线程调度。
  4. 使用线程是加锁先后和解锁先后要按优先级顺序避免死锁,同时也切忌两次加锁产生死锁。
  5. 线程函数无入参无返回值,当一个类成员函数作为线程函数时必须用static修饰,弱化成员函数,让成员函数没有this指针。

线程同步方式:

  1. 信号,使用方法和进程几乎一样,但是是另一套相似的API,不可以互换。
  2. 信号量,和进程类似,功能和互斥锁基本一样。
  3. 互斥锁,保护临界资源。
  4. 控制变量,常和互斥锁配合使用,控制线程执行的先后。暂时挂起线程还锁,解决线程为获得数据等待其他线程,导致长时间占用锁。

 

至此,总结了进程和线程使用时要注意的地方和较为齐全的各自的同步方式。面试官的最爱内容,后续博文将用代码逐一实现他们!