在python的多线程和多进程编程中,当多个线程或进程对同一个对象同时进行访问或修改时,会发生线程或进程安全问题。
对于线程,由于不同的线程可以共享内存,所以对于同一个变量的访问往往容易造成线程安全问题。只要线程之间存在资源竞争,就会存在线程安全的可能性。比如,对于一个int型变量a,每个线程都会对其进行+1操作,那么如果一个线程在另一个线程获取到该变量的引用后并在内存中新建该变量的值之前,对该变量进行修改,那么后面的线程会覆盖前面线程的那次操作,从而使得改变量被少加一次,使得该变量的值不符合我们的预期,这就是线程安全问题。
对于进程,由于不同的进程之间具有独立的内存,因此对于每个进程内的变量的修改不会影响其他的进程,但是有时候,对于一些进程间共享的变量或者系统文件、终端输出等对象,也会存在进程安全问题。往往的,对于进程间的共享变量,需要特定的构造方式来实现,而这些构造方式往往又是进程安全的;而对于系统文件或者控制台输出(sys.stdout),由于不同进程之间对这些对象是存在资源竞争关系的,所以会存在进程安全问题。比如,对于一个系统文件,多个进程进行写入操作,有可能在一个进程正在写入的时候,另一个进程也同时写入,从而会造成乱码或者写入不规范等和预期不一致的结果。
要解决线程或者进程安全问题,一般就是通过加锁来实现同步,避免资源的不合理竞争。也可以使用一些队列、管道等线程和进程安全的工具来实现同步,而这些工具的底层也是锁机制。
但是加锁后,一个潜在的危险是可能引发死锁,造成程序一直阻塞,无法继续运行。所谓死锁,就是进程或线程之间由于循环竞争被占用的资源而造成的无限阻塞的现象,比如,线程A占用了资源S1,且是被上锁的,只有A自己释放后其他线程才可以获得S1,而A对S1的释放又需要获得S2,但是S2同时被线程B占用,同样也上锁,而B对S2的释放又需要获得S1,这样,就会导致A一直等待B释放S2,但B同时又在等A释放S1,这样互相等待,双方谁也无法释放占用的资源,造成一直阻塞,形成死锁。所以,死锁的形成需要:对竞争的资源加锁,并且不同的线程或进程在占用这资源的同时,发生了循环请求。这句话实际上就是百度上关于死锁产生的四个必要条件的总结。