python学习40——并发编程之多线程
1 线程开启方式
第一种:用Thread类创建线程对象
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=(‘xilou‘,)) t.start() # 创建线程的开销非常小 几乎是代码一执行线程就已经创建了 print(‘主‘)
第二种:通过继承Thread类建自己的线程类
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(‘over‘) if __name__ == ‘__main__‘: t = MyThead(‘xilou‘) t.start() print(‘主‘)
2 TCP服务端实现并发
服务端:
import socket from threading import Thread from multiprocessing import Process """ 服务端 1.要有固定的IP和PORT 2.24小时不间断提供服务 3.能够支持并发 """ server =socket.socket() # 括号内不加参数默认就是TCP协议 server.bind((‘127.0.0.1‘,8080)) server.listen(5) # 将服务的代码单独封装成一个函数 def talk(conn): # 通信循环 while True: try: data = conn.recv(1024) # 针对mac linux 客户端断开链接后 if len(data) == 0: break print(data.decode(‘utf-8‘)) conn.send(data.upper()) except ConnectionResetError as e: print(e) break conn.close() # 链接循环 while True: conn, addr = server.accept() # 接客 # 叫其他人来服务客户 # t = Thread(target=talk,args=(conn,)) t = Process(target=talk,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‘))
3 线程对象的join方法
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(‘主‘)
4 线程间数据共享
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)
结果
666
666
说明同一进程下的多线程数据是共享的
5 线程对象
active_count:当前正在活跃的线程数
current_thread().name:获取线程名字
6 守护线程
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(‘主‘)
"""
主线程运行结束之后不会立刻结束 会等待所有其他非守护线程结束才会结束
因为主线程的结束意味着所在的进程的结束
"""
7 线程互斥锁
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)
8 GIL全局解释器锁
"""
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.解释型语言的通病:同一个进程下多个线程无法利用多核优势
"""
9 多进程与多线程的实际应用场景
"""
多线程是否有用要看具体情况
单核:四个任务(IO密集型\计算密集型)
多核:四个任务(IO密集型\计算密集型)
"""
# 计算密集型 每个任务都需要10s
单核(不用考虑了)
多进程:额外的消耗资源
多线程:介绍开销
多核
多进程:总耗时 10+
多线程:总耗时 40+
# IO密集型
多核
多进程:相对浪费资源
多线程:更加节省资源
# 计算密集型 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)