线程的理论
线程的理论知识
1.什么是线程
一条流水线的工作流程
之前描述的进程 : 在内存中开启一个进程空间,然后将主进程的所有资源数据复制一份,然后调用 CPU 执行这些代码
之前描述的进程不够具体,具体的应该是这样: 在内存中开启一个进程空间,然后将主进程的所有资源数据复制一份,然后调用线程去执行这些代码
进程是资源单位,线程是执行单位(线程是 CPU 最小的执行单位,进程是由线程执行的)
以后你描述一个开启一个进程 :在内存中开启一个进程空间,然后将主进程的所有资源数据复制一份,然后调用线程去执行这些代码
2.线程 VS 进程
- 开启进程的开销非常大,比开启线程的开销大很多
- 开启线程的速度非常快,要大几十甚至几百倍
- 线程线程之间可以共享数据,进程进程之间必须借助队列等方法实现通信
3.线程的应用
并发 : 一个 CPU 看起来像是执行多个任务
单个进程开启三个进程
开启三个进程并发的执行任务
文本编辑器
- 输入文字
- 在屏幕上显示
- 保存在磁盘中
开启多线程就非常好了 就非常好了 因为你要是打开三个窗口 ,不方便
数据共享,开销小,速度快
4.开启线程的两种方式
第一版
from threading import Thread
import time
def 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 Thread
import time
class 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 Thread
import time
class 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 Thread
import os
def task():
print(os.getpid())
if __name__ == '__main__':
t1 = Thread(target = task)
t2 = Thread(target = task)
t1.start()
t2.start()
print(f"主线程{os.getpid()}")
'''
59998
59998
主线程59998 不变不变
6.同一个进程,线程是共享数据的
- 进程:子进程和主进程是隔离的
- 线程:共享
- 同一进程的资源数据对于这个进程的多个线程来说是共享的
from threading import Thread
import os
x = 3
def task():
global x
x = 100
if __name__ == '__main__':
t1 = Thread(target = task)
t2 = Thread(target = task)
t1.start()
t2.start()
print(f"主线程:{x}")
# 主线程:100 你看改变了 不是
7.线程的其他方法
几个方法全部都在里了哦
from threading import Thread
from threading import currentThread
from threading import enumerate
from threading import activeCount
import os
import time
x = 3
def 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 Thread
import time
def 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 Thread
import time
def 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 Process
import time
def 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('===主')
'''
===主
123
456
end456
'''
我们看一下守护线程,结果很秀
简单版本的
from threading import Thread
import time
def 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 Thread
import time
def 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 = True
t1.start()
t2.start()
print("main------")
'''
123
456
main------
end123
end456
'''
注意end123 输出来了
将上面时间改一下,结果就变了
from threading import Thread
import time
def 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 = True
t1.start()
t2.start()
print("main------")
'''
123
456
main------
end456
'''
总结 : 守护线程等待非守护线程以及主线程结束后结束
PS : 就是主线程等其他进程,守护进程等待主线程
需要重点理解的,还没加锁的情况
from threading import Thread
import time
import random
x = 100
def task():
time.sleep(random.randint(1,2))
global x
temp = x
time.sleep(random.randint(1, 2))
temp = temp - 1
x = temp
if __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 Thread
from threading import Lock
import time
import random
x = 100
def 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 这是最终的结果
'''