OK ,看完进程,接下来看线程,线程是干什么呢的,跟进程有什么区别?
进程有很多优点,它提供了多道编程,让我们感觉我们每个人都拥有自己的CPU和其他资源,可以提高计算机的利用率。很多人就不理解了,既然进程这么优秀,为什么还要线程呢?其实,仔细观察就会发现进程还是有很多缺陷的,主要体现在两点上:
-
-
进程只能在一个时间干一件事,如果想同时干两件事或多件事,进程就无能为力了。
-
进程在执行的过程中如果阻塞,例如等待输入,整个进程就会挂起,即使进程中有些工作不依赖于输入的数据,也将无法执行。
-
如果这两个缺点理解比较困难的话,举个现实的例子也许你就清楚了:如果把我们上课的过程看成一个进程的话,那么我们要做的是耳朵听老师讲课,手上还要记笔记,脑子还要思考问题,这样才能高效的完成听课的任务。而如果只提供进程这个机制的话,上面这三件事将不能同时执行,同一时间只能做一件事,听的时候就不能记笔记,也不能用脑子思考,这是其一;如果老师在黑板上写演算过程,我们开始记笔记,而老师突然有一步推不下去了,阻塞住了,他在那边思考着,而我们呢,也不能干其他事,即使你想趁此时思考一下刚才没听懂的一个问题都不行,这是其二。
现在你应该明白了进程的缺陷了,而解决的办法很简单,我们完全可以让听、写、思三个独立的过程,并行起来,这样很明显可以提高听课的效率。而实际的操作系统中,也同样引入了这种类似的机制——线程。
看见下边目录了吧,不少吧,没事,进程线程从程序方法上都是如出一辙的,只是字母不一样而已
只要进程学明白了,线程就很好理解
- 线程的创建的两种方式
- 查看线程的进程id
- 验证线程是数据共享的
- 多进程和多线程的效率对比
- 锁 同步 互斥锁
- 死锁现象
- 递归锁
- 多线程的程序不结束多进程的程序不结束的区别
- 守护线程
- 子进程不能input
记住线程的单词,跟导入的单词字母,跟进程不一样
上代码
from threading import Thread #线程的导入
# from multiprocessing import Process #进程的导入,看清除了哈
第一种方式
def f1(n):
print('%s号线程任务'%n)
def f2(n):
print('%s号线程任务'%n)
if __name__ == '__main__':
t1 = Thread(target=f1,args=(1,))
t2 = Thread(target=f2,args=(2,))
t1.start()
t2.start()
print('主线程')
第二种创建方式
class MyThread(Thread):
def __init__(self,name):
super(MyThread, self).__init__()
super().__init__()
self.name = name
def run(self):
print('hello girl :' + self.name)
if __name__ == '__main__':
t = MyThread('alex')
t.start()
print('主线程结束')
看吧基本上都一样,没太大区别,当然本质上是有区别的
import os
from threading import Thread
from multiprocessing import Process
def f1(n):
print('1号',os.getpid()) #这个功能没啥意思,把单词记住
print('%s号线程任务'%n)
def f2(n):
print('2号',os.getpid())
print('%s号线程任务'%n)
if __name__ == '__main__':
t1 = Thread(target=f1,args=(1,))
t2 = Thread(target=f2,args=(2,))
t1.start()
t2.start()
print('主线程',os.getpid())
print('主线程')
import os
import time
from threading import Thread
# from multiprocessing import Process
#通过对全局变量的修改来验证线程之间是数据共享的,共享同一进程中的数据
num = 100
def f1(n):
time.sleep(3)
global num
num = 3
print('子线程的num',num)
if __name__ == '__main__':
t = Thread(target=f1,args=(1,))
t.start()
#等待子线程运行结束才继续往下执行
t.join()
print('主线程中的num:',num)
看吧,最后结果跟进程的不一样,(主线程,跟子线程是数据共享的)。
import time
from threading import Thread
from multiprocessing import Process
def f1():
# time.sleep(1) #io密集型
# 计算型:
n = 10
for i in range(10000000):
n = n + i
if __name__ == '__main__':
#查看一下20个线程执行20个任务的执行时间
t_s_time = time.time()
t_list = []
for i in range(5):
t = Thread(target=f1,)
t.start()
t_list.append(t)
[tt.join() for tt in t_list]
t_e_time = time.time()
t_dif_time = t_e_time - t_s_time
#查看一下20个进程执行同样的任务的执行时间
p_s_time = time.time()
p_list = []
for i in range(5):
p = Process(target=f1,)
p.start()
p_list.append(p)
[pp.join() for pp in p_list]
p_e_time = time.time()
p_dif_time = p_e_time - p_s_time
print('多线程的执行时间:',t_dif_time)
print('多jincheng的执行时间:',p_dif_time)
结果就是线程的运行速度是要比进程快的,这就是为什么要用线程的原因
from multiprocessing import Queue
import queue
import time
from threading import Lock, Thread
num = 100
def f1(loc):
# num -= 1
loc.acquire()
global num
# num -= 1
tmp = num
tmp -= 1
time.sleep(0.001)
num = tmp
loc.release()
if __name__ == '__main__':
t_loc = Lock()
t_list = []
for i in range(10):
t = Thread(target=f1,args=(t_loc,))
t.start()
t_list.append(t)
[tt.join() for tt in t_list]
print('主线的num',num)
锁,把线程锁住,别乱套
进程也有死锁与递归锁,在进程那里忘记说了,放到这里一切说了额,进程的死锁和线程的是一样的,而且一般情况下进程之间是数据不共享的,不需要加锁,由于线程是对全局的数据共享的,所以对于全局的数据进行操作的时候,要加锁。
所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,如下就是死锁
import time
from threading import Thread, Lock, RLock
def f1(locA,locB):
locA.acquire()
print('f1>>1号抢到了A锁')
time.sleep(1)
locB.acquire()
print('f1>>1号抢到了B锁')
locB.release()
locA.release()
def f2(locA,locB):
locB.acquire()
print('f2>>2号抢到了B锁')
locA.acquire()
time.sleep(1)
print('f2>>2号抢到了A锁')
locA.release()
locB.release()
if __name__ == '__main__':
locA = Lock()
locB = Lock()
t1 = Thread(target=f1,args=(locA,locB))
t2 = Thread(target=f2,args=(locA,locB))
t1.start()
t2.start()
最后结果就是
f1>>1号抢到了A锁
f2>>2号抢到了B锁
解决方法,递归锁,在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。
这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。上面的例子如果使用RLock代替Lock,则不会发生死锁:
impor time
from threading import Thread, Lock, RLock
def f1(locA, locB):
# print('xxxx')
# time.sleep(0.1)
locA.acquire()
print('f1>>1号抢到了A锁')
time.sleep(1)
locB.acquire()
print('f1>>1号抢到了B锁')
locB.release()
locA.release()
def f2(locA, locB):
print('22222')
time.sleep(0.1)
locB.acquire()
print('f2>>2号抢到了B锁')
locA.acquire()
time.sleep(1)
print('f2>>2号抢到了A锁')
locA.release()
locB.release()
if __name__ == '__main__':
#locA =locB = Lock()
#locA = Lock()
#locB = Lock()
locA = locB = RLock() #递归锁,维护一个计数器,acquire一次就加1,release就减1
t1 = Thread(target=f1, args=(locA, locB))
t2 = Thread(target=f2, args=(locA, locB))
t1.start()
t2.start()
最后结果就是
f1>>1号抢到了A锁
22222 #主线程的快一点
f1>>1号抢到了B锁
f2>>2号抢到了B锁
f2>>2号抢到了A锁
import time
from threading import Thread
from multiprocessing import Process
#守护进程:主进程代码执行运行结束,守护进程随之结束 #这两行字是重点,好好理解一下,敲一下
#守护线程:守护线程会等待所有非守护线程运行结束才结束 #代码,看一下结果,对比一下
def f1():
time.sleep(2)
print('1号线程')
def f2():
time.sleep(3)
print('2号线程')
if __name__ == '__main__':
# t1 = Thread(target=f1,)
# t2 = Thread(target=f2,)
# t1.daemon = True
# t2.daemon = True
# t1.start()
# t2.start()
# print('主线程结束')
t1 = Process(target=f1, )
t2 = Process(target=f2, )
# t1.daemon = True
# # t2.daemon = True
t1.start()
t2.start()
print('主进程结束')
上面两行字是重点
import time
from threading import Thread
from multiprocessing import Process
def f1():
time.sleep(2)
print('1号线程')
def f2():
time.sleep(3)
print('2号线程')
if __name__ == '__main__':
t1 = Thread(target=f1,)
t2 = Thread(target=f2,)
# t1.daemon = True #方法,这就是守护进程
t2.daemon = True
t1.start()
t2.start()
print('主线程结束')
# t1 = Process(target=f1, )
# t2 = Process(target=f2, )
# t1.daemon = True
# # t2.daemon = True
# t1.start()
# t2.start()
#
# print('主进程结束')
from threading import Thread
from multiprocessing import Process
def f1():
name = input('请输入名字') #EOFError: EOF when reading a line
#
print(name)
# if __name__ == '__main__':
# input('主进程输入')
# p = Process(target=f1,)
# p.start()
#
# print('主进程结束')
# if __name__ == '__main__':
# input('主进程输入')
p = Thread(target=f1,)
p.start()
print('主进程结束')
子进程里面不能input,会报错
子线程里面可以。
线程知识点方法有点多,但还是那句话,跟进程差不太多,好好练一下,敲一下代码,单词记住,加油吧。