线程是一个资源单位,真正被CPU执行的其实是进程里面的线程,例如打开word是打开一个进程,那么在里执行写,读,插入图片等操作就是进行一个个线程操作;
通俗理解:进程类似于工厂,线程类似于是工厂里面的一条条流水线,所有的进程肯定是最少有一个线程;
进程间数据默认是隔离的,但是同一个进程内的多个线程数据是共享的;
- 开设进程需要做哪些操作
重新申请一块内存空间 将所需的资源全部导入
- 开设线程需要做哪些操作
上述两个步骤都不需要 所以开设线程消耗的资源远比开设进程的少
from threading import Thread import time def test(name): print(f'{name}is running!') time.sleep(2) print(f'{name}is over!') # 开设线程可以不用在main判断语句下开设 t = Thread(target=test,args=('Hammer',)) t.start() print('主进程') # 线程不需要考虑内存空间和资源的问题,也不需要等待主进程执行完再执行子进程等问题 # 所以开设线程所需要的时间更短,速度更快
# 和进程类似,继承的方式 class Myclass(Thread): def __init__(self,name): super().__init__() self.name = name def run(self): print(f'{self.name} is running !') time.sleep(2) print(f'{self.name} is over !') t = Myclass('Hammer') t.start()
在进程中的join方法是,主进程等待子进程执行完再运行;
线程中顾名思义是一样的,主线程等待子线程执行完再执行
from threading import Thread import time def test(name): print(f'{name} is running!') time.sleep(2) print(f'{name} is over! ') # 开设线程 t = Thread(target=test,args=('子线程',)) t.start() t.join() print('主线程')
通过os.getpid()方法获取进程号来验证两个线程同属于一个进程
from threading import Thread import time import os def test(name): print(os.getpid()) # 9436 print(f'{name} is running!') time.sleep(2) print(f'{name} is over! ') # 开设线程 t = Thread(target=test,args=('子线程',)) t.start() # 9436 print(os.getpid()) print('主线程')
统计当前活跃的线程数
from threading import Thread,active_count import time def test(name): print(f'{name} is running !') time.sleep(2) print(f'{name} is over !') t = Thread(target=test,args=('线程1',)) t1 = Thread(target=test,args=('线程2',)) t.start() t1.start() print(active_count()) # 3 # 现有活跃线程数为3的原因是主线程加2个子线程
获取当前线程的名字
from threading import Thread,current_thread import time def test(name): print(f'子线程名:>>>{current_thread().name}') print(f'{name} is running !') time.sleep(2) print(f'{name} is over!') t = Thread(target=test,args=('线程1',)) t1 = Thread(target=test,args=('线程2',)) t.start() t1.start() print(f'主线程名:>>> {current_thread().name}') # 结果 子线程名:>>>Thread-1 线程1 is running ! 子线程名:>>>Thread-2 线程2 is running ! 主线程名:>>> MainThread 线程2 is over! 线程1 is over! # 子线程可以通过t.name直接拿,主线程必须通过方法.name拿
类似守护进程一样,一个线程结束其他线程也得结束(陪葬)
主线程的结束意味着整个进程的结束,所以主线程需要等待里面所有非守护线程的结束才能结束
from threading import Thread import time def test(name): print(f'{name} is running ') time.sleep(2) print(f'{name} is over !') t = Thread(target=test,args=('子线程',)) t.daemon = True # 守护线程必须放在start上 t.start() print('主线程')
迷惑的例子,判断打印顺序
from threading import Thread from multiprocessing import Process import time def foo(): print(123) time.sleep(3) print("end123") def bar(): print(456) time.sleep(1) print("end456") if __name__ == '__main__': t1=Thread(target=foo) t2=Thread(target=bar) t1.daemon=True t1.start() t2.start() print("main-------") # 结果 123 456 main------- end123 end456 # 结果分析 开设线程直接打印123,然后遇到sleep方法 打印456,遇到sleep方法 打印main 这时候主线程没有直接结束,等待非守护线程(bar)结束才能结束,然而bar()方法中,sleep睡了3秒,那么print('end123')睡1秒就打印了
验证同一个进程内线程间数据共享
注意是同一进程下!
from threading import Thread money = 100 def test(): global money money = 999 t = Thread(target=test) t.start() t.join() print(money) # 结果 999 # 这样是修改了的,和进程不一样
多个线程修改数据和多进程修改数据一样容易出现数据错乱,这样需要线程互斥锁来解决这个问题;
对比进程互斥锁中的查票买票代码理解;
from threading import Thread, Lock from multiprocessing import Lock import time num = 100 def test(mutex): global num mutex.acquire() # 抢锁 # 先获取num的数值 tmp = num # 模拟延迟效果 time.sleep(0.1) # 修改数值 tmp -= 1 num = tmp mutex.release() # 释放锁 t_list = [] # 声明锁 mutex = Lock() for i in range(100): t = Thread(target=test, args=(mutex,)) t.start() t_list.append(t) # 确保所有的子线程全部结束 for t in t_list: t.join() print(num) # 如果不加锁,那么输出的num是99,比如减100次应该是0,在这里并发操作不加锁会产生数据的错乱,需要变成串行
服务端
import socket from threading import Thread from multiprocessing import Process server = socket.socket() server.bind(('127.0.0.1', 8080)) server.listen(5) # 和用户交互的代码封装 def talk(sock): while True: try: data = sock.recv(1024) if len(data) == 0: break print(data.decode('utf8')) sock.send(data + b'Hi!') except ConnectionResetError as e: print(e) break sock.close() while True: sock, addr = server.accept() print(addr) # 开设多进程或者多线程 t = Thread(target=talk, args=(sock,)) t.start()
客户端
import socket client = socket.socket() client.connect(('127.0.0.1', 8080)) whlie True: client.send(b'hello') data = client.recv(1024) print(data.decode('utf8'))