一、概念
1.什么是进程
计算机程序只不过是磁盘中可执行的二进制(或其他类型)的数据。它们只有在被读取到内存中,被操作系统调用的时候才开始它们的生命期。
进程(有时被称为重量级进程)是程序的一次执行。每个进程都有自己的地址空间、内存、数据栈及其它记录其运行轨迹的辅助数据。
操作系统管理在其上运行的所有进程,并为这些进程公平的分配时间,进程也可以通过fork和spawn操作来完成其它的任务。
不过各个进程有自己的内存空间、数据栈等,所以只能使用进程间通讯,而不能直接共享信息。
2.什么是线程
线程(有时候被称为轻量级进程)跟进程有些相似,不同的是,所有的线程运行在同一个进程中,共享相同的运行环境。
线程有开始,顺序执行和结束三部分。它有一个自己的指令指针,记录自己运行到什么地方。
线程的运行可能被抢占(中断),或暂时的被挂起(也叫睡眠),让其他的线程运行,这叫做让步。
一个进程中的各个线程之间共享同一片数据空间,所以线程之间可以比进程之间更方便地共享数据以及相互通讯。
线程一般都是并发执行的,正是由于这种并行和数据共享的机制使得多个任务的合作变为可能。
实际上,在单CPU的系统中,真正的并发是不可能的,每个线程会被安排成每次只运行一小会,然后就把CPU让出来,让其他的线程去运行。
每个线程都只做自己的事,在需要的时候跟其它的线程共享运行的结果。
当然,这样的共享并不是完全没有危险的。如果多个线程共享访问同一片数据,则由于数据访问的顺序不一样,有可能导致数据结果的不一致问题。这叫做竞态条件。
幸运的是,大多数线程库都带有一系列的同步原语,来控制线程的执行和数据的访问。
3.进程与线程的关系
一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。
相对进程而言,线程是一个更加接近于执行体的概念,它可以与同进程中的其它线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。
进程和线程的主要区别在于它们是不同的操作系统资源管理方式。
进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。
线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,
所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大。
但对于一些要求同时进行并且又要共享某些变量的并发操作,只能使用线程,不能用进程。
(1)简而言之,一个程序至少有一个进程,一个进程至少有一个线程。
(2)线程的划分尺度小于进程,使得多线程程序的并发性高。
(3)另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,而极大的提高了程序的运行效率。
(4)线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。
但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
(5)从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。
但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的主要区别。
线程和线程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正好相反。线程适合于在SMP机器上运行,而进程则可以跨机器迁移。
4.并行与并发
并行:两个或多个事件在同一时刻发生;
并发:两个或多个事件在同一时间间隔内发生。
在操作系统中个,并发是指一个时间段中有几个程序都处于已启动到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行。
在网络服务器上,并发是指同一时刻能处理的连接数。比如服务器建立了100个TCP连接,即服务器同时维护了100个socket,那么并发量就是1000。
并发的关键是有处理多个任务的能力,不一定要同时。
并行的关键是有同时处理多个任务的能力。
5.同步和异步
同步:发送方发送数据后,等待接收方发回响应以后才发下一个数据包的通讯方式。步骤一致。
异步:发送方发送数据后,不等待接收方发回响应,接着发送下一个数据包的通讯方式。
同步是阻塞模式,异步是非阻塞模式。
二、锁
1.lock
进程同步锁
我们知道进程一般都是并发执行,有时候并发执行特别混乱,必须让进程串行,虽然可以使用join,但是join不太灵活,这里就会使用到同步锁。
#进程同步锁 from multiprocessing import Process,Lock import os,time,random def work(lock): #上同一把锁 lock.acquire() print('%s is running'% os.getpid()) time.sleep(random.randint(1,3)) print("%s is done"% os.getpid()) lock.release() if __name__ == "__main__": lock = Lock() #和join一样,一把锁 #建一把锁,将其放在函数之中,每个进程都会调用 p1 = Process(target=work,args=(lock,)) p2 = Process(target=work,args=(lock,)) p3 = Process(target=work,args=(lock,)) p1.start() p2.start() p3.start() #执行结果: 4292 is running #一个进程执行完毕之后再执行下一个 4292 is done 11748 is running 11748 is done 6184 is running 6184 is done
线程互斥锁
我们知道线程是共享一个进程的地址空间的,改变地址空间中的环境变量就会改变所有线程的环境变量,但是如果我们想用线程来更改环境变量,必须使用互斥锁。
#线程互斥锁 from threading import Thread,Lock import time n = 100 def task(): global n with lock: temp = n time.sleep(0.1) n = temp-1 if __name__ == '__main__': lock = Lock() lst = [] for i in range(50): t = Thread(target=task) lst.append(t) #创建一个线程 t.start() #启动一个线程 for t in lst: t.join() print(n)
这里可以用一个例子来说明Lock和join的关系
如果使用join,那么就只有几个人能买到票,前面的没买到,后面的查票都不行,这个添加到查询上面的的锁不合适
from multiprocessing import Process import json,random,time,os def search(): #查票 with open("db.txt",encoding='utf-8') as f: dic=json.load(f) print("%s 剩余票数 %s" %(os.getpid(),dic['count'])) def get(): #抢票 with open("db.txt",encoding='utf-8') as read_f: dic = json.load(read_f) if dic['count'] > 0: dic['count'] -=1 time.sleep(random.randint(1,3)) #模拟手速+网速 with open("db.txt",'w', encoding='utf-8') as write_f: json.dump(dic,write_f) print("