进程:资源单位 线程:执行单位 将操作系统比喻成一个大的工厂 那么进程就相当于工厂里面的车间 而线程就是车间里面的流水线 每一个进程肯定自带一个线程 再次总结: 进程:资源单位(起一个进程仅仅只是在内存空间中开辟一块独立的空间) 线程:执行单位(真正被cpu执行的其实是进程里面的线程,线程指的就是代码的执行过程,执行代码中所需要使用到的资源都找所在的进程索要) 进程和线程都是虚拟单位,只是为了我们更加方便的描述问题
开设进程 1.申请内存空间 耗资源 2.“拷贝代码” 耗资源 开线程 一个进程内可以开设多个线程,在用一个进程内开设多个线程无需再次申请内存空间操作 总结: 开设线程的开销要远远的小于进程的开销 同一个进程下的多个线程数据是共享的!!! 我们要开发一款文本编辑器 获取用户输入的功能 实时展示到屏幕的功能 自动保存到硬盘的功能 针对上面这三个功能,开设进程还是线程合适??? 开三个线程处理上面的三个功能更加的合理
from threading import Thread import time def task(name): print(‘%s is running‘%name) time.sleep(1) print(‘%s is over‘%name) # 开启线程不需要在main下面执行代码 直接书写就可以 # 但是我们还是习惯性的将启动命令写在main下面 t = Thread(target=task,args=(‘egon‘,)) t.start() # 创建线程的开销非常小 几乎是代码一执行线程就已经创建了 print(‘主‘)
from threading import Thread import time class MyThead(Thread): def __init__(self, name): """针对刷个下划线开头双下滑线结尾(__init__)的方法 统一读成 双下init""" # 重写了别人的方法 又不知道别人的方法里有啥 你就调用父类的方法 super().__init__() self.name = name def run(self): print(‘%s is running‘%self.name) time.sleep(1) print(‘egon DSB‘) if __name__ == ‘__main__‘: t = MyThead(‘egon‘) t.start() print(‘主‘)
# ----------------服务端---------------- # 1.要有固定的IP和PORT # 2.24小时不间断提供服务 # 3.能够支持并发 import socket from threading import Thread serve = socket.socket() serve.bind((‘127.0.0.1‘, 8080)) serve.listen(5) # 将服务的代码单独封装成一个函数 def run(conn): while True: try: data = conn.recv(1024) # 针对mac linux 客户端断开链接后 if not data:break print(data.decode(‘utf8‘)) conn.send(data.upper()) except Exception: break conn.close() # 链接循环 while True: conn, addr = serve.accept() t = Thread(target=run, args=(conn,)) t.start() # ----------------客户端---------------- import socket client = socket.socket() client.connect((‘127.0.0.1‘,8080)) while True: client.send(b‘hello world‘) data = client.recv(1024) print(data.decode(‘utf-8‘))
from threading import Thread import time def task(name): print(‘%s is running‘%name) time.sleep(3) print(‘%s is over‘%name) if __name__ == ‘__main__‘: t = Thread(target=task,args=(‘egon‘,)) t.start() t.join() # 主线程等待子线程运行结束再执行 print(‘主‘)
from threading import Thread import time money = 100 def task(): global money money = 666 print(money) if __name__ == ‘__main__‘: t = Thread(target=task) t.start() t.join() print(money)
from threading import Thread, active_count, current_thread import os,time def task(n): # print(‘hello world‘,os.getpid()) print(‘hello world‘,current_thread().name) time.sleep(n) if __name__ == ‘__main__‘: t = Thread(target=task,args=(1,)) t1 = Thread(target=task,args=(2,)) t.start() t1.start() t.join() print(‘主‘,active_count()) # 统计当前正在活跃的线程数 # print(‘主‘,os.getpid()) # print(‘主‘,current_thread().name) # 获取线程名字
from threading import Thread import time def task(name): print(‘%s is running‘%name) time.sleep(1) print(‘%s is over‘%name) if __name__ == ‘__main__‘: t = Thread(target=task,args=(‘egon‘,)) t.daemon = True t.start() print(‘主‘)
重点:
主线程运行结束之后不会立刻结束, 会等待所有其他非守护线程结束才会结束,因为主线程的结束意味着所在的进程的结束。
如下案例:
from threading import Thread import time def foo(): print(123) time.sleep(1) print(‘end123‘) def func(): print(456) time.sleep(3) print(‘end456‘) if __name__ == ‘__main__‘: t1 = Thread(target=foo) t2 = Thread(target=func) t1.daemon = True t1.start() t2.start() print(‘主.......‘)
运行结果:
123
456
主.......
end123
end456
from threading import Thread,Lock import time money = 100 mutex = Lock() def task(): global money mutex.acquire() tmp = money time.sleep(0.1) money = tmp - 1 mutex.release() if __name__ == ‘__main__‘: t_list = [] for i in range(100): t = Thread(target=task) t.start() t_list.append(t) for t in t_list: t.join() print(money)
""" In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.) """ python解释器其实有多个版本 Cpython Jpython Pypypython 但是普遍使用的都是CPython解释器 在CPython解释器中GIL是一把互斥锁,用来阻止同一个进程下的多个线程的同时执行 同一个进程下的多个线程无法利用多核优势!!! 疑问:python的多线程是不是一点用都没有???无法利用多核优势 因为cpython中的内存管理不是线程安全的 内存管理(垃圾回收机制) 1.应用计数 2.标记清楚 3.分代回收 重点: 1.GIL不是python的特点而是CPython解释器的特点 2.GIL是保证解释器级别的数据的安全 3.GIL会导致同一个进程下的多个线程的无法同时执行即无法利用多核优势(******) 4.针对不同的数据还是需要加不同的锁处理 5.解释型语言的通病:同一个进程下多个线程无法利用多核优势
from threading import Thread,Lock import time mutex = Lock() money = 100 def task(): global money # with mutex: # tmp = money # time.sleep(0.1) # money = tmp -1 mutex.acquire() tmp = money time.sleep(0.1) # 只要你进入IO了 GIL会自动释放 money = tmp - 1 mutex.release() if __name__ == ‘__main__‘: t_list = [] for i in range(100): t = Thread(target=task) t.start() t_list.append(t) for t in t_list: t.join() print(money)
100个线程起起来之后 要先去抢GIL
我进入io后 GIL自动释放 但是我手上还有一个自己的互斥锁
其他线程虽然抢到了GIL但是抢不到互斥锁
最终GIL还是回到你的手上 你去操作数据
之后往复直到所有线程全部执行完
多线程是否有用要看具体情况 如: 单核:四个任务(IO密集型\计算密集型) 多核:四个任务(IO密集型\计算密集型) 每个任务都需要10s # 计算密集型 (几乎一直占用cpu) 单核(不用考虑了,该时代已过) 多进程:额外的消耗资源 多线程:较少开销s 多核 多进程:(4个进程同时处理,一个进程处理一个任务)总耗时 10s多一点 多线程:(都是在一个进程里,无法利用多核优势)总耗时 40s多一点 # IO密集型 (不怎么占用cpu) 多核 多进程:相对浪费资源,因为需要开辟空间,拷贝代码 多线程:更加节省资源
# 计算密集型 from multiprocessing import Process from threading import Thread import os,time def work(): res = 0 for i in range(10000000): res *= i if __name__ == ‘__main__‘: l = [] print(os.cpu_count()) # 获取当前计算机CPU个数 start_time = time.time() for i in range(12): p = Process(target=work) # 1.4679949283599854 t = Thread(target=work) # 5.698534250259399 t.start() # p.start() # l.append(p) l.append(t) for p in l: p.join() print(time.time()-start_time) # IO密集型 from multiprocessing import Process from threading import Thread import os,time def work(): time.sleep(2) if __name__ == ‘__main__‘: l = [] print(os.cpu_count()) # 获取当前计算机CPU个数 start_time = time.time() for i in range(4000): # p = Process(target=work) # 21.149890184402466 t = Thread(target=work) # 3.007986068725586 t.start() # p.start() # l.append(p) l.append(t) for p in l: p.join() print(time.time()-start_time)
多进程和多线程都有各自的优势,我们在写项目的时候通常可以多进程下面再开设多线程,这样的话既可以利用多核也可以介绍资源消耗。
原文:https://www.cnblogs.com/BoyGc/p/12761947.html