进程有不少优势,它提供了多道编程,让咱们感受咱们每一个人都拥有本身的CPU和其余资源,能够提升计算机的利用率。不少人就不理解了,既然进程这么优秀,为何还要线程呢?其实,仔细观察就会发现进程仍是有不少缺陷的,主要体如今两点上:python
进程只能在一个时间干一件事,若是想同时干两件事或多件事,进程就无能为力了。编程
进程在执行的过程当中若是阻塞,例如等待输入,整个进程就会挂起,即便进程中有些工做不依赖于输入的数据,也将没法执行。json
线程,有时被称为轻量进程(Lightweight Process,LWP),是程序执行流的最小单元,也是操做系统可以进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运做单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中能够并发多个线程,每条线程并行执行不一样的任务。缓存
线程与进程的区别:多线程
首先须要明确的一点是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。并发
那么什么是GIL呢,下面是官方文档给出的解释:app
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.)dom
为何会有GIL的缘由:ide
一、直接调用多线程 性能
import threading import time def run(n): time.sleep(3) print('task',n) t1 = threading.Thread(target=run,args=('t1',)) t2 = threading.Thread(target=run,args=('t2',)) t1.start() t2.start()
t1.join() #等待线程t1执行完毕
PS:
#threading.current_thread() 打印当前线程
#threading.active_count() 打印当前活跃线程数
二、继承式调用多线程(通常这种用的比较少)
import threading import time class MyThread(threading.Thread): def __init__(self, num): threading.Thread.__init__(self) self.num = num def run(self): time.sleep(3) print("running on number:%s" % self.num) if __name__ == '__main__': t1 = MyThread(1) t2 = MyThread(2) t1.start() t2.start()
在线程start以前,能够把线程变成守护线程,守护线程服务于非守护线程,当非守护线程执行完毕以后,程序直接结束,不会管非守护线程是否执行完。t.setDaemon(True),t为生成的线程。
python在使用多线程的时候已经有了GIL为何还须要线程锁呢?由于python 的GIL只能控制同一时间只能有一个线程在运行,可是不能控制同一时间只有一个线程在修改数据。有人会问,既然都已经只有一个线程在运行,为何还会有多个线程在修改数据呢?咱们看下面的图。。。
import time import threading def addNum(): global num #在每一个线程中都获取这个全局变量 print('--get num:',num ) time.sleep(1) num -=1 #对此公共变量进行-1操做 num = 100 #设定一个共享变量 thread_list = [] 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 )
PS:不要在3.x上运行,3.x上的结果老是正确的,多是内部加了锁
正常来说,这个num结果应该是0, 但在Linux的python 2.7上多运行几回,会发现,最后打印出来的num结果不老是0,为何每次运行的结果不同呢? 哈,很简单,假设你有A,B两个线程,此时都 要对num 进行减1操做, 因为2个线程是并发同时运行的,因此2个线程颇有可能同时拿走了num=100这个初始变量交给cpu去运算,当A线程去处完的结果是99,但此时B线程运算完的结果也是99,两个线程同时CPU运算的结果再赋值给num变量后,结果就都是99。那怎么办呢? 很简单,每一个线程在要修改公共数据时,为了不本身在还没改完的时候别人也来修改此数据,能够给这个数据加一把锁, 这样其它线程想修改此数据时就必须等待你修改完毕并把锁释放掉后才能再访问此数据。
import time import threading def addNum(): global num # 在每一个线程中都获取这个全局变量 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)
PS:就算之后用3.x的版本也要本身加锁,即便不加锁结果正确也要加,由于官方文档没有明确指出3.x的版本内部加了锁。
说白了就是锁中还有锁的时候,就不能简单地使用互斥锁,要使用递归锁。
import threading 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() 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)
互斥锁同时只容许一个线程更改数据,而Semaphore是同时容许必定数量的线程更改数据 。
import threading, time def run(n): semaphore.acquire() time.sleep(1) print("run the thread: %s\n" % n) semaphore.release() num = 0 semaphore = threading.BoundedSemaphore(5) # 最多容许5个线程同时运行 for i in range(20): t = threading.Thread(target=run, args=(i,)) t.start() while threading.active_count() != 1: pass else: print('----all threads done---') print(num)
事件处理的机制:全局定义了一个“Flag”,若是“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞;若是“Flag”值为True,那么执行event.wait 方法时便再也不阻塞。
用 threading.Event 实现线程间通讯,使用threading.Event可使一个线程等待其余线程的通知,咱们把这个Event传递到线程对象中,
Event默认内置了一个标志,初始值为False。一旦该线程经过wait()方法进入等待状态,直到另外一个线程调用该Event的set()方法将内置标志设置为True时,该Event会通知全部等待状态的线程恢复运行。
经过Event来实现两个或多个线程间的交互,下面是一个红绿灯的例子,即起动一个线程作交通指挥灯,生成几个线程作车辆,车辆行驶按红灯停,绿灯行的规则。
import threading, time import random def light(): if not event.isSet(): #初始化evet的flag为真 event.set() #wait就不阻塞 #绿灯状态 count = 0 while True: if count < 10: print('\033[42;1m---green light on---\033[0m') elif count < 13: print('\033[43;1m---yellow light on---\033[0m') elif count < 20: if event.isSet(): event.clear() print('\033[41;1m---red light on---\033[0m') else: count = 0 event.set() #打开绿灯 time.sleep(1) count += 1 def car(n): while 1: time.sleep(random.randrange(3, 10)) #print(event.isSet()) if event.isSet(): print("car [%s] is running..." % n) else: print('car [%s] is waiting for the red light...' % n) event.wait() #红灯状态下调用wait方法阻塞,汽车等待状态 if __name__ == '__main__': car_list = ['BMW', 'AUDI', 'SANTANA'] event = threading.Event() Light = threading.Thread(target=light) Light.start() for i in car_list: t = threading.Thread(target=car, args=(i,)) t.start()
queue.
Queue
(maxsize=0) #先入先出queue.
LifoQueue
(maxsize=0) #last in fisrt out queue.
PriorityQueue
(maxsize=0) #存储数据时可设置优先级的队列队列经常使用方法
Queue.
qsize
()
Queue.
empty
() #return True if empty
Queue.
full
() # return True if full
Queue.
put
(item, block=True, timeout=None) # priority_number, data
Queue.put
_nowait
()
Queue.
get
()
Queue.
get_nowait
()
import queue q2 = queue.Queue() q1 = queue.LifoQueue() q = queue.PriorityQueue() q.put((8,'alex')) q.put((6,'wusir')) q.put((7,'json')) print(q.get()) print(q.get()) print(q.get()) print(q.get(timeout=3)) #自由修改数据本身测试