线程的理论知识
1.什么是线程
一条流水线的工作流程
之前描述的进程 : 在内存中开启一个进程空间,然后将主进程的所有资源数据复制一份,然后调用 CPU 执行这些代码
之前描述的进程不够具体,具体的应该是这样: 在内存中开启一个进程空间,然后将主进程的所有资源数据复制一份,然后调用线程去执行这些代码
进程是资源单位,线程是执行单位(线程是 CPU 最小的执行单位,进程是由线程执行的)
以后你描述一个开启一个进程 :在内存中开启一个进程空间,然后将主进程的所有资源数据复制一份,然后调用线程去执行这些代码
2.线程 VS 进程
- 开启进程的开销非常大,比开启线程的开销大很多
- 开启线程的速度非常快,要大几十甚至几百倍
- 线程线程之间可以共享数据,进程进程之间必须借助队列等方法实现通信
3.线程的应用
并发 : 一个 CPU 看起来像是执行多个任务
单个进程开启三个进程
开启三个进程并发的执行任务
文本编辑器
- 输入文字
- 在屏幕上显示
- 保存在磁盘中
开启多线程就非常好了 就非常好了 因为你要是打开三个窗口 ,不方便
数据共享,开销小,速度快
4.开启线程的两种方式
第一版
from threading import Threadimport timedef task(name): print(f"{name} is running") time.sleep(1) print(f"{name} is gone")if __name__ == '__main__': t1 = Thread(target = task,args=("海狗",)) t1.start() print("====主线程")# 执行的速度非常快
第二版
没传参数的情况
from threading import Threadimport timeclass MyThread(Thread): def run(self): print(f"{self.name} is running") time.sleep(1) print(f"{self.name} is gone")if __name__ == '__main__': t1 = MyThread () t1.start() print("====主线程") '''Thread-1 is running====主线程Thread-1 is gone 类似于进程中不传 self.name 输出的结果是 process - 1'''
传参数的情况
from threading import Threadimport timeclass MyThread(Thread): def __init__(self,name,l1,s1): super().__init__() self.name = name self.l1 = l1 self.s1 = s1 def run(self): print(f"{self.name} is running") time.sleep(1) print(f"{self.name} is gone")if __name__ == '__main__': t1 = MyThread ('魏无羡',[1,2,3],"180") t1.start() print("====主线程")'''魏无羡 is running====主线程魏无羡 is gone'''
5.线程进程 pid
主线程和子线程没有地位之分,这就引出了一个问题?
一个进程究竟谁在干活?
一个主线程在干活,当干完活了,你得等待其他线程干完活之后,才能结束本线程.
一个进程下的主线程和主进程共用一个 pid 可以这样理解,他们在一个空间里嘛
from threading import Threadimport osdef task(): print(os.getpid())if __name__ == '__main__': t1 = Thread(target = task) t2 = Thread(target = task) t1.start() t2.start() print(f"主线程{os.getpid()}")'''5999859998主线程59998 不变不变
6.同一个进程,线程是共享数据的
- 进程:子进程和主进程是隔离的
- 线程:共享
- 同一进程的资源数据对于这个进程的多个线程来说是共享的
from threading import Threadimport osx = 3def task(): global x x = 100if __name__ == '__main__': t1 = Thread(target = task) t2 = Thread(target = task) t1.start() t2.start() print(f"主线程:{x}")# 主线程:100 你看改变了 不是
7.线程的其他方法
几个方法全部都在里了哦
from threading import Threadfrom threading import currentThreadfrom threading import enumeratefrom threading import activeCountimport osimport timex = 3def task(): time.sleep(1) print(666)if __name__ == '__main__': t1 = Thread(target = task,name = '线程 1') t2 = Thread(target = task,name = '线程 2') # 设置线程名 t1.start() t2.start() time.sleep(1) print(t1.isAlive()) # 判断线程是否活着 返回 True or False # print(t1.getName()) # 获取线程名 # t1.setName('子线程-1') # 修改线程名 # print(t1.name) # 获取线程名 重要**************************************** # print(currentThread()) # 获取当前线程的对象,并且是主线程哦 # print(enumerate()) # 返回一个列表,包含所有的线程对象 # print(activeCount()) # 返回活着的线程数 重要*************************************** # print(f"===主线程{os.getpid()}")
8.join 与守护线程
并发的情况
from threading import Threadimport timedef task(name): print(f"{name} is running") time.sleep(1) print(f"{name} is gone")if __name__ == '__main__': start_time = time.time() t1 = Thread(target = task,args = ('海狗',)) t2 = Thread(target = task,args = ('海狗1',)) t3 = Thread(target = task,args = ('海狗2',)) t1.start() t2.start() t3.start() t1.join() t2.join() t3.join() print(f"主线程{time.time() - start_time}")'''海狗 is running海狗1 is running海狗2 is running海狗 is gone海狗1 is gone海狗2 is gone主线程1.0073320865631104
串行的情况 : 一个一个的执行
from threading import Threadimport timedef task(name): print(f"{name} is running") time.sleep(1) print(f"{name} is gone")if __name__ == '__main__': start_time = time.time() t1 = Thread(target = task,args = ('海狗',)) t2 = Thread(target = task,args = ('海狗1',)) t3 = Thread(target = task,args = ('海狗2',)) t1.start() t1.join() t2.start() t2.join() t3.start() t3.join() print(f"主线程{time.time() - start_time}")'''海狗 is running海狗 is gone海狗1 is running海狗1 is gone海狗2 is running海狗2 is gone主线程3.0137791633605957'''
回忆一下守护进程
from multiprocessing import Processimport timedef foo(): print(123) time.sleep(1) print('end123') # 随着主进程的结束这个就不执行了def bar(): print(456) time.sleep(1) print('end456')if __name__ == '__main__': p1 = Process(target = foo) p2 = Process(target = bar) p1.daemon = True p1.start() p2.start() print('===主')'''===主123456end456'''
我们看一下守护线程,结果很秀
简单版本的
from threading import Threadimport timedef sayhi(name): print('你滚') time.sleep(2) print("%s say hello"%name)if __name__ == '__main__': t = Thread(target = sayhi,args = ('大黄',)) t.setDaemon(True) # 必须在t.start()之前 # t.daemon = True 这个也是 t.start() print("主线程")'''你滚主线程'''
下面两版作对比
from threading import Threadimport timedef foo(): print(123) time.sleep(1) print('end123')def bar(): print(456) time.sleep(1) print('end456')t1 = Thread(target = foo)t2 = Thread(target = bar)t1.daemon = Truet1.start()t2.start()print("main------")'''123456main------end123end456'''注意end123 输出来了
将上面时间改一下,结果就变了
from threading import Threadimport timedef foo(): print(123) time.sleep(3) print('end123')def bar(): print(456) time.sleep(1) print('end456')t1 = Thread(target = foo)t2 = Thread(target = bar)t1.daemon = Truet1.start()t2.start()print("main------")'''123456main------end456'''
总结 : 守护线程等待非守护线程以及主线程结束后结束
PS : 就是主线程等其他进程,守护进程等待主线程
需要重点理解的,还没加锁的情况
from threading import Threadimport timeimport randomx = 100def task(): time.sleep(random.randint(1,2)) global x temp = x time.sleep(random.randint(1, 2)) temp = temp - 1 x = tempif __name__ == '__main__': l1 = [] for i in range(100): t = Thread(target = task) l1.append(t) t.start() #t.join() 这里的结果是串行,自己测试的 for i in l1: i.join() ''' 放在这里的效果相当于并发 就像是之前的那个代码,1 2 3 join放在下面 一样 但 要是紧跟着,那就是串行,这里还是并发 运行速度非常快,一窝蜂全部拿到 x = 100 不是其他人不执行,而是 结果都是一样的 99 ''' print(f"主线程:{x}")
多个任务公抢一个资源时,要让其串行,所以选择加锁
from threading import Threadfrom threading import Lockimport timeimport randomx = 100def task(lock): lock.acquire() global x temp = x time.sleep(0.01) temp = temp - 1 # 这样写的原因是这一步执行太快,所以写开 x = temp lock.release()if __name__ == '__main__': mutex = Lock() l1 = [] for i in range(100): t = Thread(target = task,args = (mutex,)) l1.append(t) t.start() time.sleep(3) print(f'主进程{x}')'''主进程0 这是最终的结果'''