python 多线程
什么是线程:
在一个进程中,默认就有一个线程。将进程比喻成工厂车间的话,线程就相当于车间里的一条流水线。进程负责将资源整合到一起,而线程才是cpu上面的执行单位。
多线程:
一个进程中存在多个线程,多个线程共享该进程中的地址空间,相当于车间内的多条流水线,共享车间内的所有资源。
进程和线程的区别:
线程共享创建它的进程的地址空间。而进程有自己单独的地址空间。
线程共享其进程资源,而进程完全copy其父进程资源。
进程中的线程与线程可以直接通信,进程必须依赖进程间通信与同级进程通信。
创建线程的开销远远小于进程,不用重复开辟内存空间。
开启线程的两种方式:
#方式一 from threading import Thread def talk(): print("%s is running"%os.getpid()) if __name__ == '__main__': t = Thread(target=talk) t.start() print('主') #方式二 from threading import Thread import os class MyThread(Thread): def __init__(self,name): super().__init__() self.name = name def run(self): print("pid %s name [%s] is running"%(os.getpid(),self.name)) if __name__ == '__main__': t = MyThread("egon") t.start() print("主",os.getpid())
同一进程下开启多线程和多进程的区别:
from multiprocessing import Process from threading import Thread def talk(): start = time.time() print("%s is running"%os.getpid()) print(time.time()-start) if __name__ == '__main__': #多进程,每个进程都有不同的ip t1 = Process(target=talk) t2 = Process(target=talk) t3 = Process(target=talk) t1.start() t2.start() t3.start() #多线程,每个线程的ip都和主线程一样 t4 = Thread(target=talk) t5 = Thread(target=talk) t6 = Thread(target=talk) t4.start() t5.start() t6.start() print('主',os.getpid())
练习:
#服务端 import threading from socket import * phone = socket(AF_INET,SOCK_STREAM) phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) phone.bind(("192.168.19.115",8888)) phone.listen(5) def talk(conn,addr): while True: try: data = conn.recv(1024) if not data:break conn.send(data.upper()) except Exception: break conn.close() if __name__ == '__main__': while True: conn,addr = phone.accept() p = threading.Thread(target=talk,args=(conn,addr)) p.start() #客户端 from socket import * c = socket(AF_INET,SOCK_STREAM) c.connect(('192.168.19.115',8888)) while True: msg = input(">>>:".strip()) if not msg:continue c.send(msg.encode("utf-8")) data = c.recv(1024) print(data.decode('utf-8')) c.close()
from threading import Thread input_lis = [] format_lis = [] def talk(): while True: msg = input(">>>:").strip() if not msg:continue input_lis.append(msg) def format(): while True: if input_lis: res = input_lis.pop() format_lis.append(res.upper()) def save(): with open("db.txt",'a')as f: while True: if format_lis: f.write("%s\n"%(format_lis.pop())) f.flush() if __name__ == '__main__': t1 = Thread(target=talk) t2 = Thread(target=format) t3 = Thread(target=save) t1.start() t2.start() t3.start()
线程的其它属性和方法:
from threading import Thread,currentThread,activeCount import os,time,threading def talk(): print("%s is running "%(currentThread().getName())) if __name__ == '__main__': t = Thread(target=talk) t = Thread(target=talk,name = "egon") t.start() print(t.name)#线程名 print(t.getName()) print(t.is_alive()) print(currentThread().getName())#默认线程名 print(threading.enumerate())#返回一个列表,同activeCount time.sleep(3) print(t.is_alive())#是否存活 print("主",activeCount())#个数
守护线程:
无论是守护进程还是守护线程,都要遵循:守护进程(线程)会随着主进程(线程)运行结束而销毁。
对进程来说:运行结束就是主进程代码运行结束
对线程来说:运行结束必须等到所有的非守护线程运行结束后,主线程才算运行结束,因为主线程的结束以为这该进程的结束,进程的整体资源都将被回收。
from threading import Thread,currentThread import time def talk1(): time.sleep(10)#这里如果是小于或等于非守护进程的睡眠时间,将会执行下面代码 print("%s is running"%currentThread().getName())#10秒时间足以让主线程和非守护线程执行结束,这句不会打印,随着主线程结束而结束 def talk2(): time.sleep(2) print("%s is running"%currentThread().getName()) if __name__ == '__main__': t1 = Thread(target=talk1) t2 = Thread(target=talk2) t1.daemon = True t1.start() t2.start() print('主程序执行完成')#守护线程随着主线程的结束而结束,注意主线程是当所有的非守护线程结束后才会结束
全局解释锁GIL:
GIL的本质还是一把互斥锁,所有的互斥锁的本质都是一样,那就是将并发转成串行,以此来控制同一时间内共享数据只能被一个任务修改,从而保护数据的安全性。
保护不同的数据安全,就应该加不同的锁
GIL是把同一进程内,多个线程争抢一把锁,保证同一时刻只有一个线程在运行
在线程执行过程中与I/O,运行时间过长的时候会强制释放GUL供其他线程使用。知道该线程再次抢到GUL,基于上次继续运行。
from threading import Thread,Lock import time n = 100 def work(): global n tem = n time.sleep(0.5) n = tem-1 if __name__ == '__main__': mutex = Lock() t_lis = [] s = time.time() for i in range(100): t = Thread(target=work) t_lis.append(t) t.start() for t in t_lis: t.join() print("%s:%s"%(time.time()-s,n)) # 0.5403599739074707:99执行了100次但是结果是99,出现数据错乱 from threading import Thread,Lock import time n = 100 def work(): mutex.acquire() global n tem = n time.sleep(0.5) n = tem-1 mutex.release() if __name__ == '__main__': mutex = Lock() t_lis = [] s = time.time() for i in range(100): t = Thread(target=work) t_lis.append(t) t.start() for t in t_lis: t.join() print("%s:%s"%(time.time()-s,n)) # 50.05104112625122:0加上应用层互斥锁,运行时间长,数据安全