1 什么是线程?
线程被称作轻量级的进程,对于线程来说,因为有了GIL,所以没有真正的并行,那么什么是GIL呢,GIL:全局解释锁(只有Cpython解释器才有)
计算机的执行单位以线程为单位,线程是计算机的最小可执行单位。
进程是资源分配的基本单位,线程是可被调度的基本单位。
线程不可以拥有独立的资源,线程的执行,必须依赖进程的资源。
一个进程中,必须有一个线程。
进程由 代码段 数据段 PCB组成(process control block)
线程由 代码段 数据段 TCB组成(thread control block)
线程又分为用户级线程和内核级线程(了解)
用户级线程:对于程序员来说的,这样的线程完全被程序员控制执行,调度
内核级线程:对于计算机内核来说的,这样的线程完全被内核控制调度。
2 线程和进程的关系又是什么?
thread - 线程
import thread 操作线程的模块
import threading 用这个去操作线程
(1) cpu切换进程要比cpu切换线程 慢很多
在python中,如果IO操作过多的话,使用多线程最好了
(2) 在同一个进程内,所有线程共享这个进程的pid,也就是说所有线程共享所属进程的所有资源和内存地址
(3) 在同一个进程内,所有线程共享该进程中的全局变量
(4) 因为有GIL锁的存在,在Cpython中,没有真正的线程并行。但是有真正的多进程并行
当你的任务是计算密集的情况下,使用多进程好
总结:在CPython中,IO密集用多线程(快),计算密集用多进程
(5)关于守护线程和守护进程的事情(注意:代码执行结束并不代表程序结束)
守护进程:要么自己正常结束,要么根据父进程的代码执行结束而结束
守护线程:要么自己正常结束,要么根据父线程的执行结束而结束,注意不是跟随父进程代码结束而结束
3 主线程会随着子线程的结束而结束,守护线程会随着主线程的结束而结束,所以顺序是:子线程先结束,然后主线程结束,然后守护线程结束
代码块
from threading import Thread
from multiprocessing import Process
import time
def func():
time.sleep(2)
print(123)
def func1():
time.sleep(1)
print('abc')
# 守护线程是根据主线程执行结束才结束
# 守护线程不是根据主线程的代码执行结束而结束
# 主线程会等待普通线程执行结束,再结束
# 守护线程会等待主线程结束,再结束
# 所以,一般把不重要的事情设置为守护线程
# 守护进程是根据主进程的代码执行完毕,守护进程就结束
if __name__ == '__main__':
t = Process(target=func)
t.daemon = True #设置为守护进程
t.start() #守护线程开启
t1 = Process(target=func1)
t1.start()
print(99999999999999999999)
4 开启线程的两种方法
4-1第一种方法:
代码块
import threading
from threading import Thread
import time
def func():
print('这是一个子线程')
time.sleep(2)
if __name__ == '__main__':
t = Thread(target=func,args=())
t.start()
4-2利用继承开启第二种方法:
代码块
import threading
import time
from threading import Thread
class Mythread(Thread):
def __init__(self):
super(Mythread,self).__init__()
def run(self):
print('我是一个线程')
m=Mythread()
m.start() #我是一个线程
5 进程和线程的对比
5-1 对比1:用线程和进程同时开启100个任务,看谁快
代码块
from multiprocessing import Process #导入进程
from threading import Thread #导入线程
import time
def func():
pass
if __name__ == '__main__':
start = time.time()
for i in range(100):
p = Process(target=func)
p.start()
print('开100个进程的时间:',time.time() - start)
start = time.time()
for i in range(100):
p = Thread(target=func)
p.start()
print('开100个线程的时间:', time.time() - start)
开100个进程的时间: 6.703976392745972
开100个线程的时间: 0.16490411758422852
5-2:子线程的pid和子进程的pid
代码块
from multiprocessing import Process
from threading import Thread
import time,os
def func(name):
print('我是一个%s,我的pid是%s'%(name,os.getpid()))
if __name__ == '__main__':
print('我是main,我的pid是%s'%(os.getpid()))
for i in range(10):
p = Process(target=func,args=('进程',))
p.start()
for i in range(10):
p = Thread(target=func,args=('线程',))
p.start()
#输出结果:
线程的pid都是6740,和main是一致的,进程的pid各种各样
我是main,我的pid是6740
我是一个进程,我的pid是16156
我是一个进程,我的pid是2540
我是一个进程,我的pid是10508
我是一个进程,我的pid是15600
我是一个进程,我的pid是204
我是一个进程,我的pid是13804
我是一个线程,我的pid是6740
我是一个线程,我的pid是6740
我是一个进程,我的pid是11104
我是一个线程,我的pid是6740
我是一个线程,我的pid是6740
我是一个线程,我的pid是6740
我是一个线程,我的pid是6740
我是一个线程,我的pid是6740
我是一个线程,我的pid是6740
我是一个线程,我的pid是6740
我是一个线程,我的pid是6740
我是一个进程,我的pid是13288
我是一个进程,我的pid是15516
我是一个进程,我的pid是11016
5-3 :用线程修改全局变量
代码块
from multiprocessing import Process
from threading import Thread,Lock
import time,os
def func():
global num
num-=1
if __name__ == '__main__':
num = 100
t_l = []
for i in range(100):
t = Thread(target=func)
t.start()
t_l.append(t)
# time.sleep(1)
[t.join() for t in t_l]
print(num)