线程
定义:一条流水线的执行过程是一个线程,一条流水线必须属于一个车间,一个车间的运行过程就是一个进程(一个进程内至少有一个线程)
进程是资源单位,而线程才是CPU上的执行单位,线程创建的开销远远小于进程
多线程:一个车间内有多条流水线,多个流水线共享该车间的资源(多线程共享一个进程的资源)
为何要创建多线程:
1. 资源共享
2. 创建开销小
开启线程的两种方式:
方式一: from threading import Thread def work(name): print('%s say hello' % name) if __name__ == '__main__': t = Thread(target=work,args=('Albert',)) t.start() print('main thread') 方式二: from threading import Thread class MyThread(Thread): def __init__(self,name): super().__init__() self.name = name def run(self): print('%s say hello' % self.name) if __name__ == '__main__': t = MyThread('Albert') t.start() print('main thread')
P.S. 主进程和主线程公用同一个PID, 验证:
from threading import Thread import os def work(): print('%s say hello' % os.getpid()) if __name__ == '__main__': t = Thread(target=work,) t.start() print('main thread:%s' % os.getpid())
多线程练习:
练习一: 服务端: import socket import threading server = socket.socket(socket.AF_INET,socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) server.bind(('127.0.0.1',8080)) server.listen(5) def action(conn): while True: try: data = conn.recv(1024) if not data: break print(data) conn.send(data.upper()) except Exception: break if __name__ == '__main__': while True: conn,addr = server.accept() p = threading.Thread(target=action,args=(conn,)) p.start() 客户端: import socket client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) client.connect(('127.0.0.1',8080)) while True: msg = input('>>>:').strip() if not msg: continue client.send(msg.encode()) back_msg = client.recv(1024) print(back_msg.decode()) 练习二: from threading import Thread data_l = [] format_data_l = [] def inp(): while True: data = input('>>>:').strip() if not data:continue data_l.append(data) def format(): while True: if data_l: data = data_l.pop() format_data = data.upper() format_data_l.append(format_data) def write(): while True: if format_data_l: data = format_data_l.pop() with open('c.txt','a',encoding='utf-8') as f: f.write(data + ' ') if __name__ == '__main__': t1 = Thread(target=inp) t2 = Thread(target=format) t3 = Thread(target=write) t1.start() t2.start() t3.start()
线程一些其他属性
import threading from threading import Thread import os def work(): print('%s say hello' % threading.current_thread().getName()) if __name__ == '__main__': t = Thread(target=work,) t.setDaemon(True) #线程的守护进程 t.start() t.join() print(threading.enumerate()) #以列表形式显示当前活跃线程 print(threading.active_count()) #活跃线程数量统计 print('main thread:%s' % threading.current_thread().getName()) #获取当前线程名称
GIL锁
由于python GIL的存在,在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势。
GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念
有了GIL的存在,同一时刻统一进程中只有一个线程被执行
结论:
对计算来说,cpu越多越好,但是对于I/O来说,再多的cpu也没用
当然对于一个程序来说,不会是纯计算或者纯I/O,我们只能相对的去看一个程序到底是计算密集型还是I/O密集型,从而进一步分析python的多线程有无用武之地
现在的计算机基本上都是多核,python对于计算密集型的任务开多线程的效率并不能带来多大性能上的提升,甚至不如串行(没有大量切换),但是,对于IO密集型的任务效率还是有显著提升的。
应用:
多线程用于IO密集型,如socket,爬虫,web
多进程用于计算密集型,如金融分析
注意:
GIL 与Lock是两把锁,保护的数据不一样,前者是解释器级别的(当然保护的就是解释器级别的数据,比如垃圾回收的数据),
后者是保护用户自己开发的应用程序的数据,很明显GIL不负责这件事,只能用户自定义加锁处理,即Lock
示例: I/O密集型 from threading import Thread from multiprocessing import Process import time import os def work(): time.sleep(1) print(os.getpid()) if __name__ == '__main__': tp_l = [] start_time = time.time() for i in range(100): tp = Thread(target=work) #run_time is 1.0190582275390625 # tp = Process(target=work) #run_time is 10.807618141174316 tp_l.append(tp) tp.start() for tp in tp_l: tp.join() stop_time = time.time() print('run_time is %s' % (stop_time - start_time)) 计算密集型 from threading import Thread from multiprocessing import Process import os import time def work(): res = 0 for i in range(100000): res+=i if __name__ == '__main__': tp_l = [] start_time = time.time() for i in range(300): # tp = Thread(target=work) # run_time is 4.402251720428467 tp = Process(target=work) # run_time is 28.153610229492188 tp_l.append(tp) tp.start() for tp in tp_l: tp.join() stop_time = time.time() print('run_time is %s' % (stop_time - start_time))
互斥锁
from threading import Thread, Lock import time n = 100 def work(): with mutex: global n temp = n time.sleep(0.1) n = temp - 1 if __name__ == '__main__': mutex = Lock() t_l = [] for i in range(100): t = Thread(target=work) t_l.append(t) t.start() for i in t_l: i.join() print(n)
死锁与递归锁
所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,如下就是死锁:
死锁示例: from threading import Thread,Lock import time class MyThread(Thread): def run(self): self.f1() self.f2() def f1(self): mutexA.acquire() print('