1. 进程和线程:
进程是一个执行中的程序,每个进程都有自己的地址空间,内存,数据栈以及其他用于追踪执行的辅助数据。
线程是在同一个进程下执行的,并共享相同的上下文。线程包括开始,执行顺序和结束三部分。一个线程中各个线程和主线程共用同一片数据空间,相比较独立的进程而言,线程间的信息共享和通信更加容易。线程一般是并发执行的。
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
1) 简而言之,一个程序至少有一个进程,一个进程至少有一个线程.
2) 线程的划分尺度小于进程,使得多线程程序的并发性高。
3) 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
4) 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
5) 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。
2. 全局解释器锁:
python在设计的时候,在主循环中同时只能有一个控制线程在执行(尽管python 解释器可以运行多个线程,但在任意给定时刻,只能有一个程序运行)。
对python虚拟机的访问是全局解释器锁(GIL)控制的。这个锁就是用来保证同时只能有一个线程运行的。
1.设置 GIL。
2.切换进一个线程去运行。
3.执行下面操作之一。
a.指定数量的字节码指令。
b.线程主动让出控制权(可以调用 time.sleep(0)来完成)。
4.把线程设置回睡眠状态(切换出线程)。
5.解锁 GIL。
6.重复上述步骤
3. python使用线程:
1 import time 2 import threading 3 def loop1(): 4 print('Start loop 1 at : ', time.ctime()) 5 time.sleep(4) 6 print('End loop 1 at : ', time.ctime()) 7 8 def loop2(): 9 print('Start loop 2 at : ', time.ctime()) 10 time.sleep(2) 11 print('End loop 2 at : ', time.ctime()) 12 13 def main(): 14 print("Startig at : ", time.ctime()) 15 #loop1() 16 #loop2() 17 threading.Thread(loop1(),()) 18 threading.Thread(loop2(), ()) 19 time.sleep(6) 20 print("All done at : ", time.ctime()) 21 22 if __name__ == '__main__': 23 main()
4. threading模块中的Thread类:
threading 模块支持守护线程,其工作方式是:守护线程一般是一个等待客户端请求服务的服务器。如果没有客户端请求,守护线程就是空闲的。如果把一个线程设置为守护线程,就表示这个线程是不重要的,进程退出时不需要等待这个线程执行完成。
a. 创建Thread实例,传给它一个函数
1 from time import ctime,sleep 2 import threading 3 4 loops = [4, 2] 5 6 def loop(nloop, nsec): 7 print('Start loop {0} at : {1}'.format(nloop+1, ctime())) 8 sleep(nsec) 9 print('End loop {0} at : {1}'.format(nloop+1, ctime())) 10 11 def main(): 12 print("Startig at : ", ctime()) 13 threads = [] 14 nloops = range(len(loops)) 15 16 for i in nloops: 17 t = threading.Thread(target=loop, args=(i, loops[i])) 18 threads.append(t) 19 20 for i in nloops: 21 threads[i].start() 22 23 for i in nloops: 24 threads[i].join() 25 26 print("All DONE at : ", ctime()) 27 28 if __name__ == '__main__': 29 main()
b. 创建Thread实例,传给它一个可以调用的类实例
1 import threading 2 from time import ctime,sleep 3 4 loops = [4, 2] 5 class ThreadFunc(object): 6 def __init__(self, func, args, name=''): 7 self.name = name 8 self.func = func 9 self.args = args 10 11 def __call__(self): 12 self.func(*self.args) 13 14 def loop(nloop, nsec): 15 print('Start loop {0} at : {1}'.format(nloop + 1, ctime())) 16 sleep(nsec) 17 print('End loop {0} at : {1}'.format(nloop + 1, ctime())) 18 19 def main(): 20 print("Startig at : ", ctime()) 21 threads = [] 22 nloops = range(len(loops)) 23 24 for i in nloops: 25 t = threading.Thread(target=ThreadFunc(loop, (i, loops[i]), loop.__name__)) 26 threads.append(t) 27 28 for i in nloops: 29 threads[i].start() 30 31 for i in nloops: 32 threads[i].join() 33 34 print("All DONE at : ", ctime()) 35 36 if __name__ == '__main__': 37 main()
5. 锁:
锁有两种状态:锁定和未锁定
只支持两个函数: 获得锁和释放锁
当多线程开始挣锁时,允许第一个获得锁的线程进入临界区,并执行代码。所有之后到达的线程将阻塞,直到第一个线程执行结束,退出临界区,并释放锁。其他等待的线程获得锁并进入临界区,那些被阻塞的线程是没有顺序的,胜出线程的选择是不确定的。
1 from atexit import register 2 from random import randrange 3 from threading import Thread, currentThread, Lock 4 from time import sleep, ctime 5 6 class CleaOutputset(set): 7 def __str__(self): 8 return (', '.join(x for x in self)) 9 10 lock = Lock() 11 loops = (randrange(2, 5) for x in range(randrange(3, 7))) 12 remaining = CleaOutputset() 13 14 def loop(nsec): 15 myname = currentThread().name 16 lock.acquire() 17 remaining.add(myname) 18 print('[%s] Started %s' % (ctime(), myname)) 19 lock.release() 20 sleep(nsec) 21 lock.acquire() 22 remaining.remove(myname) 23 print('[%s] Completed %s (%d secs)' % (ctime(), myname, nsec)) 24 print(' (remaining: %s)' % (remaining or 'NONE')) 25 lock.release() 26 27 def main(): 28 for pause in loops: 29 Thread(target=loop(), args=(pause,)).start() 30 31 @register 32 def atexit(): 33 print("All DONE at: ", ctime()) 34 35 if __name__ == '__main__': 36 main()