线程:进程中负责程序执行的执行单元。
多线程:在1个进程中存在多个线程。
进程只是用来把资源集中在一起,而线程才是cpu上的执行单位。
每个进程都会默认有一个控制线程也叫作主线程。
进程之间是竞争关系,线程之间是协作关系。
多线程和进程之间的区别?
1.线程开销小,不需要申请内存空间,创建速度快。进程需要申请内存空间,创建速度慢。
2,同一进程下的多个线程,共享该进程的地址空间。
3,改变主进程 ,无法影响子进程,改变了主线程,影响其他线程。原因(该控制线程可以执行代码从而创建新的线程,该主线程的运行周期代表了进程的运行周期)
如何开启子线程?有两种方法。
方法一:创建一个threading.Thread对象,在它的初始化函数(__init__)中将可调用对象作为参数传入。(这个方法是重点)
方法二:通过继承Thread类,重写它的run方法;(这个方法作为了解)
方法一:
from threading import Threadimport time ,os def task(): print("%s is runing"%os.getpid()) time.sleep(2) print("%s is done"%os.getpid()) if __name__=="__main__": t=Thread(target=task) t.start() print("主") #主线程运行周期代表进程的运行周期,他不能先死,他要等子线程运行完和进程意义不同
15640 is runing 主
15640 is runing
方法二:
from threading import Thread import os class My_task(Thread): def __init__(self,name): super(My_task,self).__init__() self.name=name def run(self): print("%s is runing" % os.getpid()) time.sleep(2) print("%s is done"%os.getpid()) if __name__ == '__main__': t=My_task("小红") t.start() print(t.name)
结果:
24156 is runing 小红 24156 is done
子进程不能修改主进程的变量,子线程能修改主线程的变量
范例:
n=1 def t(): global n n=15 if __name__ == '__main__': t=Thread(target=t) t.start() print(n)
结果:
15
线程主要掌握的方法有3个 :
start(),
join()
from threading import current_thread , 中有个叫current_thread .getName()这个可以看线程的名字
线程池:如果你不指定默认是CPU的个数*5,进程池如果你不指定默认是CPU的个数。
from concurrent.futures import ThreadPoolExecutor from threading import current_thread import time ,random def task(n): print("%s is running"%current_thread().getName()) time.sleep(random.randint(1,3)) return n**2 if __name__ == '__main__': t=ThreadPoolExecutor(3) objs=[] for i in range(10): obj=t.submit(task,i) objs.append(obj) t.shutdown(wait=True) for obj in objs: print(obj.result()) print("主",current_thread().getName())
结果:
ThreadPoolExecutor-0_0 is running ThreadPoolExecutor-0_1 is running ThreadPoolExecutor-0_2 is running ThreadPoolExecutor-0_1 is running ThreadPoolExecutor-0_2 is running ThreadPoolExecutor-0_0 is running ThreadPoolExecutor-0_2 is running ThreadPoolExecutor-0_0 is running ThreadPoolExecutor-0_1 is running ThreadPoolExecutor-0_0 is running 0 1 4 9 16 25 36 49 64 81 主 MainThread
异步调用:提交完任务(为该任务绑定一个回调函数),不用在原地等任务执行完拿结果,可以直接提交下一个任务。一个任务一旦完成后就会立即触发回调函数的运行。
回调函数的参数是唯一的就是它绑定的返回值。
利用多线程来重写套接字
服务端:
from threading import Thread import socket import socketserver server=socket.socket(socket.AF_INET,socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) server.bind(("192.168.12.193",8085)) server.listen(5) print("starting") def talk(conn,addr): """ 接收数据发送数据函数 :param conn: :param addr: :return: """ while True: try: data=conn.recv(1024) if not data:break conn.send(data.upper()) except ConnectionResetError: break conn.close() while True: conn,addr=server.accept() t=Thread(target=talk,args=(conn,addr)) t.start() server.close()
客户端:
import socket custom=socket.socket() custom.connect(("127.0.0.1",8080)) while True: msg=input(">>>>").strip() if not msg: continue custom.send(msg.encode("utf-8")) data=custom.recv(1024) print(data.decode("utf-8")) custom.close()
守护进程和守护线程
守护进程:当主进程运行完后,不管子进程运没运行完,子进程都将立即被终止.不管有没有其他的子进程
应用:档主进程执行完了,子进程没必要存在了.
举例子来说明:
不用守护进程的情况下:
from multiprocessing import Process import os,time def task(): time.sleep(2) print("这是线程%s"%1) if __name__ == '__main__': t=Process(target=task) t.start() print('主线程')
结果:
主线程
这是线程1
使用守护进程后:
from multiprocessing import Process import os,time def task(): time.sleep(2) print("这是线程%s"%1) if __name__ == '__main__': t=Process(target=task) t.daemon=True #注意守护进程必须在p.start()之前设置,开启守护进程不能再开启子进程 t.start() print('主线程')
结果:
主线程
注意:
- 注意守护进程必须在p.start()之前设置,
- 开启的守护进程不能再开启守护进程的子进程 因为进程之间是相互独立的.如果开启再开启子进程后,当守护进程停止后,没有人回收子进程.
守护进程死掉的时间: 当主进程代码执行完后,守护进程就会死掉
守护线程
注意:
- 守护线程必须在p.start()之间设置
- 开启的守护线程可以再开启守护进程的子进程,因为在同一个进程中,如果主线程死掉,子线程也会死掉
列如:
from threading import Thread import os,time def task(): time.sleep(2) print("这是线程%s"%1) tt=Thread(target=time.time()) #开启子线程 print(time.time()) tt.start() if __name__ == '__main__': t=Thread(target=task) t.daemon=True #守护进程必须在p.start()之前设置,守护进程不能开启子进程 t.start() print('主线程')
结果:
主线程
守护线程死掉的时间:当进程內非守护线程都运行完后他才死掉.这也就是守护线程和守护进程的区别
from threading import Thread import os,time def task(): time.sleep(2) print("这是线程%s"%1) def task1(): time.sleep(3) print("这是线程%s" % 2) if __name__ == '__main__': t1=Thread(target=task) t2=Thread(target=task1) t1.daemon=True #守护进程必须在p.start()之前设置,守护进程不能开启子进程 t1.start() t2.start() print('主线程)
结果:
主线程
这是线程1
这是线程2
threading.local
我们知道线程之间的数据是共享的,但是有没有一种技术可以让线程内的数据不共享.恰好python 为我们提供了threading.local可以实现该功能.
import threading local=threading.local() #实例化一个全局对象 local.val='main-Thread' #在该线程下给local对象添加对象属性 def process_student(): print('%s (in%s)内存地址为%s'%(local.val,threading.current_thread().getName(),id(local.val))) def process_thread(name): local.val=name process_student() if __name__ == '__main__': #开启两个子线程 t1 = threading.Thread(target=process_thread, args=('one',), name="Thread-A") t2 = threading.Thread(target=process_thread, args=('two',), name="Thread-B") t1.start() t2.start() t1.join() t2.join() print('主线程的值为%s,内存地址为%s'%(local.val,id(local.val)))#打印当前现成的额值
结果:
one (inThread-A)内存地址为1545252686736
two (inThread-B)内存地址为1545252717264
主线程的值为main-Thread,内存地址为1545252704688
从结果上看来,两个子线程并没有把local.val中的值覆盖,而是自己重新开辟了一块内存空间,来存放数据,这样就把线程之间的数据給隔离开来了.
作用: 用于保存和隔离线程之间的数据.
应用:
这个东西可以用在那些地方呢,比如下载,现在都是多线程下载了,就像酷狗那样,可以同时下载很多首歌曲,那么
就可以利用这个方法来保存每个下载线程的数据,比如下载进度,下载速度之类的
所以 如果你在开发多线程应用的时候 需要每个线程保存一个单独的数据供当前线程操作,可以考虑使用这个方法,简单有效
socketserver模块
优秀的博客https://www.cnblogs.com/MnCu8261/p/5546823.html
python共提供了两个socket模块
一个是:socket
另一个是socketserver 模块 简化了网络服务器的开发,解决了io问题。
SocketServer模块简化了编写网络服务程序的任务。同时SocketServer模块也 是Python标准库中很多服务器框架的基础。
这个模块提供了多进程和多线程的接口,但是多进程不能在Windows系统下用
服务端:
import socketserver
class MyTCPhandler(socketserver.BaseRequestHandler): def handle(self):#重写handle方法 conn=self.request #这步相当于conn addr=self.client_address #这步相当于addr print(conn,addr) while True: try: data=conn.recv(1024) if not data:break conn.send(data.upper()) except ConnectionResetError: break conn.close() if __name__ == '__main__': server=socketserver.ThreadingTCPServer(("127.0.0.1",8080),MyTCPhandler) #这一步就是做了,建立连接,开线程的工作
注意在Windows系统上不能用socketserver来开线程,在其他系统上可以用socketserver.ForkingTCPServer(("127.0.0.1",8080),MyTCPhandler)来创建
server.allow_reuse_address=True #是否允许地址的重复利用,默认为false
server.serve_forever()#一直运行
客户端:
import socket client=socket.socket() client.connect(('127.0.0.1',8080)) while True: date = input('>>>>') client.send(date.encode()) data = client.recv(1024) print('>>>', data.decode()) client.close()