进程线程对比
1、根本区别
进程是操作系统资源分配的基本单位;而线程是任务调度和执行的基本单位。
每个进程都有独立的代码和数据空间;同一类线程共享代码和数据空间。
在操作系统中能同时运行多个进程(程序);而在同一个进程中有多个线程同时执行。
内存分配方面:系统在运行的时候会为每个进程分配不同的内存空间;而对线程而言,除了CPU外,系统分配内存(线程所使用的资源来自其所属进程的资源),线程组之间只能共享资源。
包含关系:没有线程的进程可以看做是单线程的,如果一个进程内有多个线程,则执行过程不是一条线的线程共同完成的。
2、优缺点
线程执行开销小,但不利于资源的管理和保护,而进程正相反。
进程
1、进程VS程序
编写完毕的代码,在没有运行的时候,称为程序。
正在运行着的代码,称为进程。
进程除了包含代码之外还要有代码的运行环境。
2、fork()
python的os模块封装了常见的系统相关,其中就包括fork(Windows不支持),可以在python程序中轻松创建子进程。
import os
pid = os.fork()
os.fork()方法执行一次会返回两次,操作系统自动把当前进程(父进程)复制一份(子进程),然后在父进程和子进程内分别执行。
# ubuntu os.fork()方法开辟进程(复制主进程数据,将数据传入子进程)
os.fork()
print("process")
# process
# process
# 子进程返回值为0,父进程返回子进程的进程id
# getpid() 获取当前进程id,getppid()获取父进程id
# 子进程可以创建子进程
# 父子进程的执行没有先后顺序
# 在多个进程中每个进程都复制父进程的所有数据,互不影响
pid = os.fork()
print("main pid %d parent pid %d" % (os.getpid(), os.getppid()))
if pid == 0:
print("child1 pid %d parent pid %d" % (os.getpid(), os.getppid()))
pid2 = os.fork()
if pid2 == 0:
print("child2 pid %d parent pid %d" % (os.getpid(), os.getppid()))
else:
print("child1 pid %d parent pid %d" % (os.getpid(), os.getppid()))
else:
print("main pid %d" % (os.getpid()))
3、multiprocessing模块
"""
Process生成进程实例
target指明进程入口函数
p1.pid得到进程的id
进程函数执行完毕进程执行完毕释放资源
主进程结束后会结束开启的子进程
join()方法会阻塞进程(join方法在哪个进程写就阻塞哪个进程)
terminate():终止进程
is_alive():判断进程是否结束
"""
from multiprocessing import Process
import os,time
# p1 进程入口函数
def p1process():
while True:
time.sleep(1)
print("进程p1执行了")
def p2process():
while True:
time.sleep(1)
print("进程p2执行了")
def main():
# 1 创建进程:创建Process类的实例
# 2 通过target指定进程入口函数
p1 = Process(target=p1process)
# 3 开启进程
p1.start()
# 阻塞主进程,此时p2不会执行,把p1中的循环去掉就可以执行p2了
p1.join()
p2 = Process(target=p2process, name="pp1")
p2.start()
# 终止进程
p1.terminate()
print(p1.is_alive(), p2.is_alive())
4、进程传参
"""
进程之间传参
一个进程修改不会影响其他进程
提供元素参数 args和字典参数kwargs
"""
from multiprocessing import Process
import time
def processmain(pam1, **kwargs):
print("++", pam1)
# ++ [1]
pam1.append(2)
print(pam1, kwargs)
# [1, 2] {'name': 'liuhaoran'}
def main():
list1 = [1]
p1 = Process(target=processmain, args=(list1, ), kwargs={"name": "liuhaoran"})
p1.start()
time.sleep(1)
p1.join()
print(list1)
# [1]
if __name__ == "__main__":
main()
5、进程封装
"""
当调用进程对象的start方法会执行进程的run方法,
如果在进程子类重写run方法
此时自定义的入口函数就不会执行了
run方法才是真正的入口函数
"""
from multiprocessing import Process
def mainprocess():
print("自定义入口函数")
class MyProcess(Process):
# def __init__(self):
# Process.__init__(self)
def run(self):
print("执行了")
def main():
p1 = MyProcess(target=mainprocess)
p1.start()
if __name__ == "__main__":
main()
6、进程池Pool
初始化进程池时需要给定最大进程数,当请求Pool时会根据当前池子中获取进程,如果有可用进程则执行,如果没有可用进程则等待,直到有可用进程。
from multiprocessing import Pool
import os
import time
# print(os.cpu_count())
# 4
# 创建入口函数
def process(i):
while True:
time.sleep(2)
print("pid:", os.getpid(), "ppid:", os.getppid())
print(i)
if __name__ == "__main__":
print("pid", os.getpid(), "ppid", os.getppid())
# 构造一个进程池
pool = Pool(4)
for i in range(20):
# 异步调用不阻塞主进程
pool.apply_async(process, args=(i,))
# 关闭进程池,不再接受新的任务
pool.close()
# 不关任务是否完成,立即终止
time.sleep(5)
print("进程已经开启了5秒,即将终止")
pool.terminate()
# pool.apply_async(process)
# ValueError: Pool not running
# 阻塞主进程,等待子进程的退出,必须在close或terminate之后使用
pool.join()
print("finish")
7、Queue进程间通信
(1)Queue用法
from multiprocessing import Queue
q = Queue(5,)
q.put(1)
q.put(2)
q.put(3)
q.put(4)
q.put(5)
print(q.qsize())
try:
# 阻塞3秒后强行放入数据,此时引发错误
q.put(6, block=True, timeout=3)
# 不阻塞进程,强行放入数据
q.put(6, block=False)
# 或者使用put_nowait()方法
q.put_nowait(6)
except Exception as e:
print("error")
q.get()
q.get()
q.get()
q.get()
q.get()
try:
q.get(block=True, timeout=2)
q.get(block=False)
q.get_nowait()
except Exception as e:
print("get error")
(2)使用Queue共享数据
"""
使用队列在进程间共享数据
"""
from multiprocessing import Queue, Process
import time
# 获取任务写入队列
def pwrite(taskqueue):
for i in range(10):
time.sleep(2)
taskqueue.put(i)
# print(taskqueue.qsize())
# 从队列读取任务
def pread(taskqueue):
while True:
# get取不到任务会阻塞读进程
task = taskqueue.get()
print("读取到任务", task)
if __name__ == "__main__":
# 构建10个任务队列
taskQueue = Queue(10)
pw = Process(target=pwrite, args=(taskQueue,))
pw.start()
print("写进程开启 pid", pw.pid)
pr = Process(target=pread, args=(taskQueue,))
pr.start()
print("读进程开启 pid", pr.pid)
pw.join()
pr.join()
print("finish")
(3)进程池下共享Queue
from multiprocessing import Pool, Manager
import time
def read(q):
while True:
r = q.get()
print(r)
time.sleep(0.1)
def write(q):
for i in range(5):
q.put(i)
time.sleep(2)
if __name__ == "__main__":
q = Manager().Queue(5)
q.put(-2)
q.put(-1)
pool = Pool(5)
pool.apply_async(write, (q,))
pool.apply_async(read, (q,))
pool.close()
pool.join()
print("finish")
多线程-threading
1、使用threading模块
多线程并发花费时间较短,创建好的线程需要start()启动。
from threading import Thread
import time
def prt():
print("hello world")
time.sleep(1)
def timecount(f):
def tc():
start = time.time()
f()
end = time.time()
print(end - start)
return tc
@timecount
def threadmain():
list1 = []
for i in range(5):
i = Thread(target=prt)
i.start()
list1.append(i)
print(list1)
for l in list1:
l.join()
@timecount
def main():
for i in range(5):
prt()
if __name__ == "__main__":
# main()
threadmain()
2、获取线程数
import threading
import time
def fun():
time.sleep(1)
print("hello")
if __name__ == "__main__":
t = threading.Thread(target=fun)
t.start()
while True:
print(threading.enumerate())
time.sleep(0.2)
3、threading封装
import threading
class MyThread(threading.Thread):
def run(self):
print("hello world")
if __name__ == "__main__":
t = MyThread()
t.start()
4、threading共享全局变量
一个进程内的线程直接可以共享全局变量
import threading
num = 1
def fun1():
num = 2
print(num) # 2
fun1()
print(num) # 1
def fun2():
global num
num = 3
print(num) # 3
fun2()
print(num) # 3
def fun3():
global num
num = 4
print(num) # 4
if __name__ == "__main__":
t = threading.Thread(target=fun3)
t.start()
print(num) # 4
多个线程的同时操作共享变量会造成数据混乱
5、互斥锁
当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制。
线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。
(1)互斥锁状态
# create
lock1 = threading.Lock()
# lock
lock1.acquire()
# release
lock1.release()
(2)使用互斥锁
import threading
import time
num = 0
def fun1():
global num
for i in range(100000):
lock1.acquire()
num += 1
lock1.release()
def fun2():
global num
for r in range(100000):
lock1.acquire()
num += 1
lock1.release()
if __name__ == "__main__":
lock1 = threading.Lock()
t1 = threading.Thread(target=fun1)
t1.start()
t2 = threading.Thread(target=fun2)
t2.start()
time.sleep(2)
print(num)
优点:
确保了某段关键代码只能由一个线程从头到尾完整的执行。
缺点:
阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了。由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时,可能会造成死锁。
(3)死锁
import threading
import time
def fun1():
if lock1.acquire():
# print("fun1")
time.sleep(2)
if lock2.acquire():
print("finish")
def fun2():
if lock2.acquire():
# print("fun2")
time.sleep(2)
if lock1.acquire():
print("finish")
if __name__ == "__main__":
lock1 = threading.Lock()
lock2 = threading.Lock()
t1 = threading.Thread(target=fun1)
t2 = threading.Thread(target=fun2)
t1.start()
t2.start()
(4)TheadLocal
"""
ThreadLocal是全局变量,每个线程都只能读写自己线程的独立副本,互不干扰
"""
import threading
def thread():
tlocal.age = 22
print(tlocal.age)
if __name__ == "__main__":
tlocal = threading.local()
tlocal.name = "liuhaoran"
print(tlocal.name)
t1 = threading.Thread(target=thread)
t1.start()
t1.join()
print(tlocal.age)
# AttributeError: '_thread._local' object has no attribute 'age'