1,线程:线程是计算机执行的最小单位,故城为轻量级的进程,所以计算机的执行单位以线程为单位
GIL:全局解释锁(只有Cpython有解释器有)对于线程来说,因为有了GIL,所以没有真正的并行
进程与线程的区别:
1>计算机的执行单位是以线程为基本单位的,计算机的最小可执行单位是线程
2>进程是资源分配的基本单位,线程是可执行的基本单位,是可被调度的基本单位
3>线程不可以独立拥有资源,线程的执行,必须依赖于所属进程中的资源,进程中必须至少应该有一个线程
1.1>线程有分为用户级线程和内核级线程
用户级线程:对于程序员来说的,这样的线程完全被程序员控制执行,调度.
内核级线程:对于计算机内核来说,这样的线程完全被内核控制调度
1.2>线程单位组成
进程:进程段,数据段,PCB(process,control block)
线程:线程段,数据段,TCB(thread control block)
2,线程和进程的比较
2.1>导入的模块不同
进程:from multiprocessing import Process
线程:from threading import Thread
2.2>cpu切换进程的速度要比CPU切换线程慢的多(CPU是可被调度的基本单位)
在python中,如果IO操作密集就使用线程,计算密集使用进程
2.3>在同一个进程内,所有线程共享这个进程的pid,也就是说所有线程共享所属进程的所有资源和内存地址
2.4>在同一个进程内,所有线程共享该进程的全局变量
2.5>因为有GIL锁的存在,在Cpython中,没有真正的线程并行,但是有真正的进程并行,锁以当任务时计算密集型的情况下,是用多进程比较好一些
总结:在Cpython中,IO密集用多线程,计算密集急用多进程
2.6>关于守护进程和守护线程的事情(正确打开代码结束和程序结束的方式,:代码执行完毕是指程序的某一行代码执行了,它并不关心这行代码代码本身所代表的含义代码的执行完毕是一个表面的概念,而程序执行完毕是指本程序整个任务执行完毕,一个程序完整的运行完毕包括它包含的代码,比如导入模块和它锁嵌入的所有程序都运行结束,才算程序结束是一个实质性的概念)
守护进程:要么自己正常结束,要么根据父进程的代码执行结束而结束(守护进程和主进程是表面兄弟)
守护线程:要么自己正常结束,要么根据父线程的执行结束而结束(守护线程和主线程是内在兄弟)
主线程会等普通线程执行结束,再结束,锁以一般把不中要的事情设置为守护线程
3,子线程的创造
3.1>from threading import Thread......................从threading这个包里导入Thread这个模块(类)
import time...............................导入时间模块
import os..................................到如操作系统模块
def func():..................................当一个生成子线程的方法
print("这是一个子线程...,子线程的pid值是:%s"os.getpid())
if __name__ == "__main__":
t = Thread(target=func)....................实例化一Thread(线程)的对象
t.start()..............................开启一个子线程
time.sleep(1)...........................程序运行到这休眠1秒钟
print("这里是主线程....主线程的pid值是:%s"%os.get())
主线程的pid值(进程号)和子线程的pid值(进程号)是一致的,一位它们都隶属于一个进程,和进程的pid值是一致的(这是有进程和线程的关系决定的)
3.2>from threading import Thread..........导入线程的模块
import time
class MyThread(Thread).....................定义一个类继承Thread这个类
def __init__(self):.....................定义一个__init__的方法
super(MyThread,self).__init__().................执行父类的__init__方法
def run(self):...................................定义run方法开启子线程
print("这是一个子线程,")
if __name__ == "__main__":
t = MyThread()...................实例化定义的类(继承Thread)
t.start()......................开启子线程自动执行run方法
print("这里是主线程...")
3.3>守护线程的开启
from threading import Thread...............导入Thread个模块
import time................................导入模块
def func():...........................开启守护子线程的方法
time.sleep(3)
print("这是守护线程")
def func1():..................................开启普通线程的方法
time.sleep(1)
print("这时普通子线程")
if __name__ == "__main__":
t1 = Thread(target=func).....................实例化一个线程
t1.daemon = True.......................将实例化的这个线程设置为守护线程
t1.start()....................................开启这个守护线程
t2 = Thread(target=func1).......................实例化一个线程
t2.start()..........................开启这个普通的子线程
print("这时主线程")
守护线程会随着主线程的的真正结束而结束
4,进程与线程的比较
4.1>开启进程的效率与开启线程的效率比较
from threading import Thread from multiprocessing import Process import time def func(): pass if __name__ == "__main": p_start = time.time() for in range(100): p = Process(target=func) p.start() print(time.time() - p_start) for i in range(100): t_time = time.time() t = Thread(target=func) print(time.time() - t_time)
4.2>线程与进程的pid值的对比
from multiprocessing import Process from threading import Thread import os def func(name): print("这是一个%s,我的pid值是%s"%(name,os.getpid())) if __name__ == "__main__": print("我是main,我的pid值是%s"%os.getpid()).................这个是main进程的pid值 for i in range(10):................................创造10个进程,并获的他们的pid值 p = Process(target=func) p.start() for i in range(10):...................................创造10个线程并获取他们的pid值 t = Thread(target=func) t.start()
由此可见,进程的所有线程和main的进程号(pid)值是一样的,而开启的每一个进程都时候不一样的.
4.3>线程与进程的join比较
from threading import Thread import time def func(num): globle num ..........................申明全局变量(把全局变量拿来进性改变) tmp = num time.sleep(0.1) num = tmp - 1.....................在子进程里对公共资源的进行改变 if __tname__ == "__main__": num = 100.......................定义一个全局变量(公共资源) t_l = [] for i in range(100): t = Thread(target=func) t.start() t_l.append(t) [t.join() for t in t_l]...................把开启的100个子线程都join主 print(num)
最后打印的结果总不是99,应为有GIL所机制的存在,对全局变量的修改有数据混乱的结果
5,线程的定时器Timer是一个模块也是一个类(继承Thread)
t = Timer(float,function)
from threading import Timer def func(): print("帅爆太阳的男人,额滴个神") if __name__ == "__main__": t = Timer(2.5,func) t.start()
6,锁机制
6.1>互斥锁Lock(一把钥匙,一把锁)
from threading import Thread,Lock import time def man(): l_tot.acquire().........................获得厕所的钥匙 print("王帆在上厕所") time.sleep(1) l_pap.acquire().........................获得纸的钥匙 print("王帆拿上卫生纸了,要操作了,请注意...") time.sleep(0.5) l_pap.release().......................还纸的钥匙 l_tot.release().....................还厕所的钥匙(这俩还的顺序可以相互调换) def woman(): l_tot.acquire().........这必须是获得的是厕所的钥匙(资源)是在ma还了以后才能获取,如果是获取的是纸的钥匙,就会陷入锁的状态 print("黄黄在上厕所") time.sleep(1.5) l_pap.acquire().............获得纸的钥匙(资源) print("黄黄拿到纸了") time.sleep(0.5) l-pap.release().............还纸的钥匙(有借有还,再借不难) l_tot.release().......................还厕所的钥匙 if __name__ == "__main__": l_tot = Lock().....................实例化一个厕所(资源)的对象,并上锁 l_pap = Lock()......................实例化一个纸的对象,并上锁 t_man = Thread(target=man) t_woman = Thread(target=woman) t_man.start() t_woman.start()
6.2>递归锁RLock()可以有无止尽的锁,但是会有一把万能钥匙
from threading import Thread,RLock.................导入线程和万能锁 import time def man(): l_tot.acquire() print("帆帆在上厕所") time.sleep(1.5) l_pap.acquire() print("帆帆拿上纸,要操作了,请注意...") time.sleep(0.5) l_pap.release() l_tot.release() def woman(): l_pap.acquire()..................这获取谁的钥匙都可以,就不影响了 print("黄黄拿到卫生纸了") time.sleep(1.5) l_tot.acquire() print("黄黄上厕所") time.sleep(1) l_pap.release() l_tot.release() if __name__ == "__main__": l_pap = l_tot = RLock() t_man = Thread(target=man) t_woman = Thread(target=woman) t_man.start() t_woman.start()
7,线程的信号量
from threading import Thread,Semaphore import time def func(sem,name,i): sem.acquire().........................获取钥匙 print("%s进入了发廊第%s次"%(name,i)) time.sleep(0.5) print("%s离开了发廊第%s次"%(name,i))
sem.release()..............................记得还钥匙 if __name__ == "__main__": sem = Semaphore(5)................实例化5把锁 for i in range(20):........................模拟20个人 t = Thread(target=func,args=(sem,"帆帆",i)) t.start()
8,线程的事件机制(Event)
form threading import Thread,Event import time import random def conn_masql(e,i) count = 1 while count <=3:..................限制while循环的次数 if e.is_set() == True:.................当条件为真(条件成立) print("第%s个人已连接成功!"% i) break print("第%s个人正在尝试重新连接..."%i) e.wait(0.5).................这时来控制是不是阻塞状态,每次循坏阻塞0.5秒 count = count+ 1 def check_mysql(): print(" 33[42m 数据库正在维护 33[0m") time.sleep(random.randint(1,2)) e.set()........................将e.is_set()的bool值改为True if __name =="__main__": e = Event()............................实例化一个时间的对象,就能用事件的东西了 t_check = Thread(target=check_mysql,args=(e,)) t_check.start() for i in range(10): t_conn = Thread(target=conn_mysql,args=(e,i)) t_conn.start()
9,条件:条件时让程序员自行去调度线程的一个机制
Condition设计4个方法:
acquire()..............获取钥匙(拿到资源)
release()...............还钥匙(释放资源)
wait()....................是指给wait发一个信号,让wait变成不阻塞(通知wait)
notify(int).................你要给多少个wait发信号
from threading import Thread,Condition def func(i) con.acquire() con.wait() print("第%s线程开启了"%i) con.release() if __name __ == "__main__": con = Condition() for i in range(10): t = Thread(target=func,args=(i,)) t.start() while 1: num = input("请输入要开启的线程数:") con.acquire() con.notify(num) con.release()