进程:qq要以一个整体的形式暴露给操作系统管理,里面包含对各种资源的调用,内存的对各种资源管理的集合,就可称之为进程。
线程:是操作系统最小的调度单位,是一串指令的集合。
进程:要操作CPU,必须要先创建一个线程,进程不能单独执行,进程执行是调动线程,至少要有一个线程;
进程是资源的集合,线程是最小的单位,所有速度没有可比性。启动线程快。
线程共享内存空间,进程的内存是相互独立的。
同一个进程的线程之间可以直接交流,两个进程想通信,必须通过一个中间代理来实现。
创建新线程很简单,创建新进程需要对其父进程进行一次克隆。
一个线程可以控制和操作同一进程里的其他线程,但是进程只能操作子进程。
使用函数定义一个线程:
import threading,time
def func(num):
print("Runing on %s threading......" %num)
time.sleep(2)
if __name__ == "__main__":
start_time = time.time()
t1 = threading.Thread(target=func,args=(1,)) #生成一个线程t1实例
t2 = threading.Thread(target=func,args=(2,))
t1.start() #启动线程t1
t2.start()
print(t1.getName()) #获取线程名
print(t2.getName())
end_time = time.time()
spend_time = end_time - start_time
print("花费时间:",spend_time)
上面代码,生成了一个线程,线程同时执行,如果正常情况下,肯定先执行t1,然后执行t2,花费的时间是4秒以上,但是使用线程,执行之间只有两秒多,如下:
Runing on 1 threading...... Thread-1 Thread-2 花费时间: 0.0005929470062255859 Runing on 2 threading......
由于上述代码是并行的,线程先执行,执行完成之后等待两秒。
使用类创建线程:
import threading,time class MyThreading(threading.Thread): '''自己创建的类,继承的是线程的类,覆盖了之前的类,因此要重写父类''' def __init__(self,num): super(MyThreading,self).__init__() #括号里面是加入参数的 '''重写父类''' self.num = num def run(self): #定义每个函数运行的函数,类的所有方法在run中,调用run,必须是run函数 print("Running task %s" %self.num) time.sleep(2) if __name__ == "__main__": t3 = MyThreading(1) #定义线程1 t4 = MyThreading(2) t3.start() #执行线程1 t4.start() print(t3.getName()) #获取线程1的名字 print(t4.getName())
上面代码,是运用类创建的线程,super(MyThreading,self).__init__(),重写类,生成新的类,使用类创建线程,所有的方法必须定义在run()函数中。
旧式类启用线程:
import threading,time
class MyThread(threading.Thread):
'''用旧式类生成线程'''
def __init__(self,num):
threading.Thread.__init__(self)
self.num = num
def run(self):
print("Start the %s threading......" %self.num)
time.sleep(2)
if __name__ == "__main__":
start_time = time.time() #程序执行起始时间
t5 = MyThread(1) #生成线程1实例
t6 = MyThread(2)
t5.start() #启动线程1
t6.start()
print(t5.getName()) #获取线程1名字
print(t6.getName()) #获取线程2名字
end_time = time.time() #程序执行结束时间
spend_time = end_time - start_time #程序执行花费时间
print(spend_time)
代码运行结果如下:
Start the 1 threading......
Start the 2 threading......
Thread-1
Thread-2
0.0004968643188476562
上面使用旧式类写的类,启动线程。
上面的花费时间,是启用线程的时间,并不是程序执行的时间。上面的代码是并行运行的。
把线程编程串行执行,即一个线程执行完毕再继续执行下一个线程,如下:
import threading,time class MyThread(threading.Thread): '''用旧式类生成线程''' def __init__(self,num): threading.Thread.__init__(self) self.num = num def run(self): print("Start the %s threading......" %self.num) time.sleep(2) if __name__ == "__main__": start_time = time.time() # 程序执行起始时间 for i in range(5): t8 = MyThread(i) #生成线程1实例 t8.start() t8.join() #等待线程执行结果,这个线程没有执行完毕,程序不执行下一步,让线程执行变成并行 end_time = time.time() #程序执行结束时间 spend_time = end_time - start_time #程序执行花费时间 print("--------all theads has finished----------------") print(spend_time)
运行结果如下:
Start the 0 threading......
Start the 1 threading......
Start the 2 threading......
Start the 3 threading......
Start the 4 threading......
--------all theads has finished----------------
10.011518001556396
从结果可以看出,上面程序执行编程了串行,等待上一个线程执行完毕之后,才会执行下一个线程,只是在里面加入一个执行,t1.join(),用来让程序等待。
下面我们让两个线程等待执行的时间不一定,如下:
import threading,time
class MyThread(threading.Thread):
'''用旧式类生成线程'''
def __init__(self,num,execute_time):
threading.Thread.__init__(self)
self.num = num
self.execute_time = execute_time
def run(self):
print("Start the %s threading......" %self.num)
time.sleep(self.execute_time)
print("Threading %s task done!" %self.num)
if __name__ == "__main__":
start_time = time.time() # 程序执行起始时间
t1 = MyThread(1,2) #生成线程1实例
t2 = MyThread(2,4)
t1.start() #执行t1
t2.start() #执行t2
t1.join() #等待线程执行结果,这个线程没有执行完毕,程序不执行下一步,让线程执行变成并行
end_time = time.time() #程序执行结束时间
spend_time = end_time - start_time #程序执行花费时间
print("--------all theads has finished----------------")
print(spend_time)
程序执行如下:
Start the 1 threading......
Start the 2 threading......
Threading 1 task done!
--------all theads has finished----------------
2.0026450157165527
Threading 2 task done!
上面程序中,线程t1和t2同时执行,但是t1.join()只等待第一个线程执行完毕,不会等待第二个线程执行完毕再往下执行,可以看出,程序向下执行完毕之后,t2还在等待执行过程中。
主线程问题,程序运行是有一个主线程在运行的。下面看一个实例:
import threading,time class MyThread(threading.Thread): '''用旧式类生成线程''' def __init__(self,num): threading.Thread.__init__(self) self.num = num def run(self): print("Start the %s threading......" %self.num) time.sleep(4) print("Threaing %s task done!!!!" %self.num) if __name__ == "__main__": start_time = time.time() # 程序执行起始时间 t_objs = [] #创建一个临时列表,先让线程都启动 for i in range(50): t8 = MyThread(i) #生成线程1实例 t8.start() t_objs.append(t8) #启动所有线程并添加列表中 #print(t_objs) print("当前活跃的线程个数:",threading.active_count()) for t in t_objs: t8.join() #所有线程启动执行完毕之后,等待所有线程执行完毕之后执行下一个线程 end_time = time.time() #程序执行结束时间 print("--------all theads has finished----------------",threading.current_thread(),threading.active_count()) spend_time = end_time - start_time #程序执行花费时间 print(spend_time)
运行结果如下:
Start the 0 threading......
Start the 1 threading......
Start the 2 threading......
Start the 3 threading......
Start the 4 threading......
Start the 5 threading......
Start the 6 threading......
Start the 7 threading......
Start the 8 threading......
Start the 9 threading......
Start the 10 threading......
Start the 11 threading......
Start the 12 threading......
Start the 13 threading......
Start the 14 threading......
Start the 15 threading......
Start the 16 threading......
Start the 17 threading......
Start the 18 threading......
Start the 19 threading......
Start the 20 threading......
Start the 21 threading......
Start the 22 threading......
Start the 23 threading......
Start the 24 threading......
Start the 25 threading......
Start the 26 threading......
Start the 27 threading......
Start the 28 threading......
Start the 29 threading......
Start the 30 threading......
Start the 31 threading......
Start the 32 threading......
Start the 33 threading......
Start the 34 threading......
Start the 35 threading......
Start the 36 threading......
Start the 37 threading......
Start the 38 threading......
Start the 39 threading......
Start the 40 threading......
Start the 41 threading......
Start the 42 threading......
Start the 43 threading......
Start the 44 threading......
Start the 45 threading......
Start the 46 threading......
Start the 47 threading......
Start the 48 threading......
当前活跃的线程个数: 51
Start the 49 threading......
Threaing 1 task done!!!!
Threaing 6 task done!!!!
Threaing 4 task done!!!!
Threaing 3 task done!!!!
Threaing 2 task done!!!!
Threaing 5 task done!!!!
Threaing 0 task done!!!!
Threaing 9 task done!!!!
Threaing 10 task done!!!!
Threaing 11 task done!!!!
Threaing 7 task done!!!!
Threaing 8 task done!!!!
Threaing 13 task done!!!!
Threaing 12 task done!!!!
Threaing 18 task done!!!!
Threaing 39 task done!!!!
Threaing 15 task done!!!!
Threaing 20 task done!!!!
Threaing 24 task done!!!!
Threaing 26 task done!!!!
Threaing 28 task done!!!!
Threaing 35 task done!!!!
Threaing 22 task done!!!!
Threaing 34 task done!!!!
Threaing 42 task done!!!!
Threaing 21 task done!!!!
Threaing 19 task done!!!!
Threaing 25 task done!!!!
Threaing 27 task done!!!!
Threaing 36 task done!!!!
Threaing 38 task done!!!!
Threaing 43 task done!!!!
Threaing 17 task done!!!!
Threaing 14 task done!!!!
Threaing 33 task done!!!!
Threaing 30 task done!!!!
Threaing 40 task done!!!!
Threaing 44 task done!!!!
Threaing 29 task done!!!!
Threaing 41 task done!!!!
Threaing 16 task done!!!!
Threaing 23 task done!!!!
Threaing 31 task done!!!!
Threaing 32 task done!!!!
Threaing 37 task done!!!!
Threaing 47 task done!!!!
Threaing 46 task done!!!!
Threaing 45 task done!!!!
Threaing 48 task done!!!!
Threaing 49 task done!!!!
--------all theads has finished---------------- <_MainThread(MainThread, started 139803255105280)> 1
4.0112504959106445
上面程序中,我们启动了50个线程,但是可以看出,其实是由51个线程在执行,其中一个是线程本身,作为主线程,线程执行完毕之后,只剩下主线程,其他线程都被杀死,要先实现并行执行线程,所有线程同时启动,并等待上一个线程执行完毕,接着执行下一个线程,要借助列表,先生成一个空的列表,把所有生成的线程添加到列表中,然后再遍历列表,依次执行启动的线程。
join()等待所有的线程执行完毕,主线程才继续执行。
设置守护线程:
正常情况下小,如果没有设置join(),程序会一直执行,不会管线程是否执行完毕,但是在主线程执行完毕之后,还是会等待其他线程执行完毕,如下:
import threading,time class MyThread(threading.Thread): '''用旧式类生成线程''' def __init__(self,num): threading.Thread.__init__(self) self.num = num def run(self): print("Start the %s threading......" %self.num) time.sleep(4) print("Threaing %s task done!!!!" %self.num) if __name__ == "__main__": start_time = time.time() # 程序执行起始时间 t_objs = [] #创建一个临时列表,先让线程都启动 for i in range(5): t8 = MyThread(i) #生成线程1实例 # t8.setDaemon(True) #把当前线程设置为守护线程 ,必须在start之前 '''程序会等待主线程执行完毕,守护线程执行完毕与否是不管的''' t8.start() t_objs.append(t8) #启动所有线程并添加列表中 #print(t_objs) print("当前活跃的线程个数:",threading.active_count()) # for t in t_objs: # t8.join() #所有线程启动执行完毕之后,等待所有线程执行完毕之后执行下一个线程 end_time = time.time() #程序执行结束时间 print("--------all theads has finished----------------",threading.current_thread(),threading.active_count()) spend_time = end_time - start_time #程序执行花费时间 print(spend_time)
运行结果如下:
Start the 0 threading......
Start the 1 threading......
Start the 2 threading......
Start the 3 threading......
Start the 4 threading......
当前活跃的线程个数: 6
--------all theads has finished---------------- <_MainThread(MainThread, started 139642438694656)> 6
0.0012087821960449219
Threaing 1 task done!!!!
Threaing 4 task done!!!!
Threaing 2 task done!!!!
Threaing 0 task done!!!!
Threaing 3 task done!!!
从上面代码运行结果可以看出,主线程执行完毕之后,又执行了其他线程,程序在执行过程中,启动了线程,但是不会在意线程是否执行完毕,没有执行完毕就继续执行,但是最后会等待其他线程执行完毕之后结束程序。如何让主线程执行完毕之后,不等待其他线程是否执行完毕就结束线程呢?这里就要用到守护线程了,如下:
import threading,time class MyThread(threading.Thread): '''用旧式类生成线程''' def __init__(self,num): threading.Thread.__init__(self) self.num = num def run(self): print("Start the %s threading......" %self.num) time.sleep(4) print("Threaing %s task done!!!!" %self.num) if __name__ == "__main__": start_time = time.time() # 程序执行起始时间 t_objs = [] #创建一个临时列表,先让线程都启动 for i in range(5): t8 = MyThread(i) #生成线程1实例 t8.setDaemon(True) #把当前线程设置为守护线程 ,必须在start之前 '''程序会等待主线程执行完毕,守护线程执行完毕与否是不管的''' t8.start() t_objs.append(t8) #启动所有线程并添加列表中 #print(t_objs) print("当前活跃的线程个数:",threading.active_count()) # for t in t_objs: # t8.join() #所有线程启动执行完毕之后,等待所有线程执行完毕之后执行下一个线程 end_time = time.time() #程序执行结束时间 print("--------all theads has finished----------------",threading.current_thread(),threading.active_count()) spend_time = end_time - start_time #程序执行花费时间 print(spend_time)
运行结果如下:
Start the 0 threading......
Start the 1 threading......
Start the 2 threading......
Start the 3 threading......
Start the 4 threading......
当前活跃的线程个数: 6
--------all theads has finished---------------- <_MainThread(MainThread, started 140133686212352)> 6
0.0010912418365478516
上面程序把所有其他线程都设置成为了守护线程,t8.setDaemon(),设置守护线程,设置守护线程必须在start()线程开始之前设置,设置完毕之后,从结果可以看出,主线程执行完毕之后,就结束了程序,不会管其他线程是否执行完毕。
全局解释器锁
Python GIL(Global Interpreter Lock)
In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)
上面的核心意思就是,无论你启多少个线程,你有多少个cpu, Python在执行的时候会淡定的在同一时刻只允许一个线程运行,擦。。。,那这还叫什么多线程呀?莫如此早的下结结论,听我现场讲。
首先需要明确的一点是GIL
并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念。就好比C++是一套语言(语法)标准,但是可以用不同的编译器来编译成可执行代码。有名的编译器例如GCC,INTEL C++,Visual C++等。Python也一样,同样一段代码可以通过CPython,PyPy,Psyco等不同的Python执行环境来执行。像其中的JPython就没有GIL。然而因为CPython是大部分环境下默认的Python执行环境。所以在很多人的概念里CPython就是Python,也就想当然的把GIL
归结为Python语言的缺陷。所以这里要先明确一点:GIL并不是Python的特性,Python完全可以不依赖于GIL。
python的线程是调用操作系统的原生线程,Python调用C语言的原生接口。
线程锁(互斥锁Mutex)
一个进程下可以启动多个线程,多个线程共享父进程的内存空间,也就意味着每个线程可以访问同一份数据,此时,如果2个线程同时要修改同一份数据,会出现什么状况?
import time import threading def addNum(): global num # 在每个线程中都获取这个全局变量 print('--get num:', num) time.sleep(1) num += 1 # 对此公共变量进行-1操作 num = 0 # 设定一个共享变量 thread_list = [] for i in range(50): t = threading.Thread(target=addNum) t.start() thread_list.append(t) time.sleep(1.000001) # for t in thread_list: # 等待所有线程执行完毕 # t.join() print('final num:', num)
运行程序如下:
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
--get num: 0
final num: 49
上面代码,执行结果有时候是50,有时候是49,如何解决呢?
用户加锁,用户加锁确保同一时间只有一个线程在修改数据,上面的代码,如果在规定时间内,没有执行完毕,那么将释放GIL,让其他线程执行,造成过多修改同一数据,因此要用户自己加锁,确保同一时间只有一个线程修改数据。
import time import threading def addNum(): global num # 在每个线程中都获取这个全局变量 print('--get num:', num) lock.acquire() #获取一把锁 time.sleep(1) #在所里面sleep()会把程序编程串行 num += 1 # 对此公共变量进行-1操作 lock.release() num = 0 # 设定一个共享变量 thread_list = [] lock = threading.Lock() for i in range(10): t = threading.Thread(target=addNum) t.start() thread_list.append(t) for t in thread_list: # 等待所有线程执行完毕 t.join() print('final num:', num)
加锁,首先声明一把锁,lock=threading.Thread(target=addNum),生成一把锁,然后在函数中加锁,lock.acquire(),加锁,lock.release(),最后释放锁,把加的锁进行释放,为什么要加锁呢?因为线程执行的时候,是有实现限制的,在规定时间如果为执行完毕,GIF会释放,加锁是为了让在同一时间内,只有同一个线程执行程序。
加锁版本
import time import threading def addNum(): global num # 在每个线程中都获取这个全局变量 print('--get num:', num) time.sleep(1) lock.acquire() # 修改数据前加锁 num -= 1 # 对此公共变量进行-1操作 lock.release() # 修改后释放 num = 100 # 设定一个共享变量 thread_list = [] lock = threading.Lock() # 生成全局锁 for i in range(100): t = threading.Thread(target=addNum) t.start() thread_list.append(t) for t in thread_list: # 等待所有线程执行完毕 t.join() print('final num:', num)
递归锁(嵌套锁)----RLock(递归锁)
说白了就是在一个大锁中还要再包含子锁
下面来看一个实例:
import threading, time def run1(): print("grab the first part data") lock.acquire() global num num += 1 lock.release() return num def run2(): print("grab the second part data") lock.acquire() global num2 num2 += 1 lock.release() return num2 def run3(): lock.acquire() #第一道锁加锁,首先进入此锁 res = run1() #进入第二道锁,第二道也有锁 print('--------between run1 and run2-----') res2 = run2() #平行进入第二道锁,和上面run1是并行锁 lock.release() print(res, res2) if __name__ == '__main__': num, num2 = 0, 0 lock = threading.Lock() for i in range(10): t = threading.Thread(target=run3) t.start() while threading.active_count() != 1: print(threading.active_count()) else: print('----all threads done---') print(num, num2)
上面代码是一把大锁里面嵌套一把小锁,这样会造成什么问题呢?如下:
11 11 11 11 11 11 ......
上面代码执行陷入了一个死循环,程序不停的执行,为什么呢?
递归锁--RLock:防止锁死
import threading, time def run1(): print("grab the first part data") lock.acquire() global num num += 1 lock.release() return num def run2(): print("grab the second part data") lock.acquire() global num2 num2 += 1 lock.release() return num2 def run3(): lock.acquire() #第一道锁加锁,首先进入此锁 res = run1() #进入第二道锁,第二道也有锁 print('--------between run1 and run2-----') res2 = run2() #平行进入第二道锁,和上面run1是并行锁 lock.release() print(res, res2) if __name__ == '__main__': num, num2 = 0, 0 lock = threading.RLock() for i in range(10): t = threading.Thread(target=run3) t.start() while threading.active_count() != 1: print(threading.active_count()) else: print('----all threads done---') print(num, num2)
运行结果如下:
grab the first part data
--------between run1 and run2-----
grab the second part data
1 1
grab the first part data
--------between run1 and run2-----
grab the second part data
2 2
grab the first part data
--------between run1 and run2-----
grab the second part data
3 3
grab the first part data
--------between run1 and run2-----
grab the second part data
4 4
grab the first part data
--------between run1 and run2-----
grab the second part data
5 5
grab the first part data
--------between run1 and run2-----
grab the second part data
6 6
grab the first part data
--------between run1 and run2-----
grab the second part data
7 7
grab the first part data
--------between run1 and run2-----
grab the second part data
8 8
grab the first part data
--------between run1 and run2-----
grab the second part data
9 9
grab the first part data
2
--------between run1 and run2-----
grab the second part data
10 10
----all threads done---
10 10
从上面代码可以看出,使用RLokc()递归所能够正确的执行退出。所以加锁多次的时候,为了防止锁死,应该使用递归锁。
Semaphore(信号量)
互斥锁 同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据 ,比如厕所有3个坑,那最多只允许3个人上厕所,后面的人只能等里面有人出来了才能再进去。
Semaphore(信号量)同一时间只允许一定数量的线程更改数据。
import threading, time def run(n): semaphore.acquire() time.sleep(1) print("run the thread: %s " % n) semaphore.release() if __name__ == '__main__': semaphore = threading.BoundedSemaphore(5) # 最多允许5个线程同时运行 '''每出来一个就会补充一个进去,限制线程里面只有一定数量的线程在执行,确保同一时间只有五个并发执行''' for i in range(17): t = threading.Thread(target=run, args=(i,)) t.start() while threading.active_count() != 1: pass # print threading.active_count() else: print('----all threads done---')
运行结果如下:
run the thread: 0
run the thread: 3
run the thread: 4
run the thread: 1
run the thread: 2
run the thread: 7
run the thread: 8
run the thread: 6
run the thread: 5
run the thread: 9
run the thread: 10
run the thread: 14
run the thread: 11
run the thread: 12
run the thread: 13
run the thread: 16
run the thread: 15
----all threads done---
上面代码中,限定了同一时间执行的线程个数,semaphore=threading.BoundedSemaphore(5)规定同一时间只有5个线程在执行,当里面有线程执行完毕之后,就会有新的线程补充进来,知道所有线程都执行完毕。
Events(事件)
事件:状态的切换,每一次状态的切换会导致其他的变化,如红绿灯。
时间:是一个非常简单的事件同步对象;一个事件需要一个内部标志。
event = threading.Event() #声明一个event()对象
event.set() #设置一个全局变量,即标志位
event.clear() #清空标志位
event.set()相当于True,event.clear()相当于False。
event.wait()检测标志位是否设置,如果没有设置,会一直卡在哪里,不往下走。如果标志位设置则不会堵塞,wait()等待标志位被设置。
If the flag is set, the wait method doesn’t do anything.
标志位设定了,代表绿灯,直接通行。
If the flag is cleared, wait will block until it becomes set again.
标志位被清空,代表红灯,wait()等待变路灯。
Any number of threads may wait for the same event.
每个线程都可以等待同一个事件。
下面来写一个红绿灯的事件:
通过Event来实现两个或多个线程间的交互,下面是一个红绿灯的例子,即起动一个线程做交通指挥灯,生成几个线程做车辆,车辆行驶按红灯停,绿灯行的规则。
import threading,time import random def light(): if not event.isSet(): event.set() #wait就不阻塞 #绿灯状态 count = 0 while True: if count < 10: print('