一、线程

　　线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中，是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流，一个进程中可以并发多个线程，每条线程并行执行不同的任务

方法：

　　start            线程准备就绪，等待CPU调度

　　setName      设置线程名称

　　getName      获取线程名称

　　setDaemon   把一个主进程设置为Daemon线程后，主线程执行过程中，后台线程也在进行，主线程执行完毕后，后台线程不论有没执行完成，都会停止

　　join              逐个执行每个线程，执行完毕后继续往下执行，该方法使得多线程变得无意义　　

　　run              线程被cpu调度后自动执行线程对象的run方法

 

threading模块

　　线程的两种调用方式：

1.直接调用（常用）

复制代码
import threading
import time

'''直接调用'''

def hello(name):
    print("Hello %s"%name)
    time.sleep(3)

if __name__ == "__main__":
    t1=threading.Thread(target=hello,args=("zhangsan",)) #生成线程实例
    t2=threading.Thread(target=hello,args=("lisi",))

    t1.setName("aaa")   #设置线程名
    t1.start()  #启动线程
    t2.start()
    t2.join()   #join  等待t2先执行完
    print("Hello")
    print(t1.getName()) #获取线程名
复制代码
2.继承式调用

复制代码
'''继承式调用'''
import threading
import time
class MyThread(threading.Thread):
    def __init__(self,name):
        threading.Thread.__init__(self)
        self.name = name
    def run(self):
        print("Hello %s"%self.name)
        time.sleep(3)

if __name__ == "__main__":
    t1=MyThread("zhangsan")
    t2=MyThread("lisi")
    t1.start()
    t2.start()
复制代码
 

setDaemon线程

复制代码
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import time
import threading
def run(n):
    print('Hello..[%s]\n' % n)
    time.sleep(2)

def main():
    for i in range(5):
        t = threading.Thread(target=run,args=[i,])
        t.start()
        t.join(1)

m = threading.Thread(target=main,args=[])
m.setDaemon(True) #将主线程设置Daemon设置为True后,主线程执行完成时,其它子线程会同时退出,不管是否执行完任务
m.start()
print("--- done----")
复制代码
 

线程锁Lock

　　一个进程下可以启动多个线程，多个线程共享父进程的内存空间，每个线程可以访问同一份数据，所以当多个线程同时要修改同一份数据时，就会出现错误

　　例如：

复制代码
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import threading
import time

num = 100 #设置一个共享变量
def show():
    global num  #在函数内操作函数外变量，需设置为全局变量
    time.sleep(1)
    num -= 1
list=[]
for i in range(100):
    t = threading.Thread(target=show)
    t.start()
    list.append(t)

for t in list:
    t.join()
print(num)
复制代码
　　上面的例子在正常执行完成后的num的结果应该是0，但实际上每次的执行结果都不太一样，因为当多个线程同时要修改同一份数据时，就会出现一些错误（只有

在python2.x运行才会出现错误，python3.x中不会），所以每个线程在要修改公共数据时，为了避免自己在还没改完的时候别人也来修改此数据，可以加上线程锁

来确保每次修改数据时只有一个线程在操作。

　　加锁代码

复制代码
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import threading
import time

num = 100 #设置一个共享变量
lock=threading.Lock()  #生成全局锁
def show():
    global num  #在函数内操作函数外变量，需设置为全局变量
    time.sleep(1)
    lock.acquire()  #修改前加锁
    num -= 1
    lock.release()  #修改后解锁
list=[]
for i in range(100):
    t = threading.Thread(target=show)
    t.start()
    list.append(t)

for t in list:
    t.join()

print(num)
复制代码
递归锁RLock

　　就是在一个大锁中再包含子锁

复制代码
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import threading
#递归锁
def run1():
    lock.acquire()  #小锁
    global num
    num +=1
    lock.release()
    return num
def run2():
    lock.acquire()  #小锁
    global  num2
    num2+=1
    lock.release()
    return num2
def run3():
    lock.acquire()  #大锁
    res = run1()
    res2 = run2()
    lock.release()
    print(res,res2)

if __name__ == '__main__':
    num,num2 = 0,0
    lock = threading.RLock()    #生成Rlock
    for i in range(10):
        t = threading.Thread(target=run3)
        t.start()

while threading.active_count() != 1:#如果不等于1，说明子线程还没执行完毕
    pass #打印进程数
else:
    print(num,num2)
复制代码
Semaphore

　　同时允许一定数量的线程更改数据

复制代码
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import threading
import time
def run(n):
    semaphore.acquire()
    time.sleep(1)
    print("run the thread: %s" %n)
    semaphore.release()

if __name__ == '__main__':
    semaphore  = threading.BoundedSemaphore(3) #设置最多允许3个线程同时运行
    for i in range(20):
        t = threading.Thread(target=run,args=(i,))
        t.start()
while threading.active_count() != 1:
    pass
else:
    print('----done---')
复制代码
event

　　实现两个或多个线程间的交互，提供了三个方法 set、wait、clear，默认碰到event.wait 方法时就会阻塞。

　　event.set()，设定后遇到wait不阻塞

　　event.clear()，设定后遇到wait后阻塞

　　event.isSet()，判断有没有被设定

复制代码
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import threading
def start():
    print("---start---1")
    event.wait()    #阻塞
    print("---start---2")

if __name__ == "__main__":
    event = threading.Event()
    t = threading.Thread(target=start)
    t.start()

    result=input(">>:")
    if result == "set":
        event.set() #设定set，wait不阻塞
复制代码
 

二、进程

multiprocessing模块

进程调用

复制代码
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from multiprocessing import Process
import time
def start(name):
    time.sleep(1)
    print('hello', name)

if __name__ == '__main__':
    p = Process(target=start, args=('zhangsan',))
    p1 = Process(target=start, args=('lisi',))
    p.start()
    p1.start()
    p.join()
复制代码
进程间通讯

　　每个进程都拥有自己的内存空间，因此不同进程间内存是不共享的，要想实现两个进程间的数据交换，有几种方法

Queue（队列）

复制代码
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from multiprocessing import Process, Queue
def start(q):
    q.put( 'hello')

if __name__ == '__main__':
    q = Queue()
    p = Process(target=start, args=(q,))
    p.start()
    print(q.get())
    p.join()
复制代码
Pipe（管道，不常用）

　　把管道的两头分别赋给两个进程，实现两个进程的互相通信

复制代码
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from multiprocessing import Process, Pipe

def start(conn):
    conn.send('hello')#发送
    print(conn.recv())#接收
    conn.close()

if __name__ == '__main__':
    parent_conn, child_conn = Pipe()    #生成一个管道
    p = Process(target=start, args=(child_conn,))
    p.start()
    print(parent_conn.recv())#接收
    parent_conn.send("11111")#发送
    p.join()
复制代码
Manager（实现了进程间真正的数据共享）

复制代码
#!/usr/bin/env python
from multiprocessing import Process, Manager
def f(dic, list,i):
    dic['1'] = 1
    dic['2'] = 2
    dic['3'] = 3
    list.append(i)

if __name__ == '__main__':
    manager = Manager()
    dic = manager.dict()#通过manager生成一个字典
    list = manager.list(range(5))#通过manager生成一个列表
    p_list = []
    for i in range(10):
        p = Process(target=f, args=(dic, list,i))
        p.start()
        p_list.append(p)
    for res in p_list:
        res.join()

    print(dic)
    print(list)
#执行结果
'''
{'2': 2, '3': 3, '1': 1}
[0, 1, 2, 3, 4, 1, 9, 2, 5, 3, 7, 6, 0, 8, 4]
'''
复制代码
 

进程池

　　进程池内部维护一个进程序列，当使用时，则去进程池中获取一个进程，如果进程池序列中没有可供使用的进程，那么程序就会等待，直到进程池中有可用进程为止。

进程池中有两个方法：

1、apply（同步）

2、apply_async（异步）

复制代码
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from  multiprocessing import Process,Pool
import time

def Foo(i):
    time.sleep(1)
    return i+100

def Bar(arg):
    print('number::',arg)

if __name__ == "__main__":
    pool = Pool(3)#定义一个进程池，里面有3个进程
    for i in range(10):
        pool.apply_async(func=Foo, args=(i,),callback=Bar)
        #pool.apply(func=Foo, args=(i,))

    pool.close()#关闭进程池
    pool.join()#进程池中进程执行完毕后再关闭,(必须先close在join)
复制代码
callback是回调函数，就是在执行完Foo方法后会自动执行Bar函数，并且自动把Foo函数的返回值作为参数传入Bar函数

 

三、协程

　　协程，又称微线程，是一种用户态的轻量级线程。协程能保留上一次调用时的状态，每次过程重入时，就相当于进入上一次调用的状态，换种说法：进入上一次离开时所处逻辑流的位置，当程序中存在大量不需要CPU的操作时（IO），适用于协程。

协程有极高的执行效率，因为子程序切换不是线程切换，而是由程序自身控制，因此，没有线程切换的开销。

不需要多线程的锁机制，因为只有一个线程，也不存在同时写变量冲突，在协程中控制共享资源不加锁，只需要判断状态就好了，所以执行效率比多线程高很多。

因为协程是一个线程执行，所以想要利用多核CPU，最简单的方法是多进程+协程，这样既充分利用多核，又充分发挥协程的高效率。

那符合什么条件就能称之为协程：1、必须在只有一个单线程里实现并发 2、修改共享数据不需加锁 3、用户程序里自己保存多个控制流的上下文栈 4、一个协程遇到IO操作自动切换到其它协程

python中对于协程有两个模块，greenlet和gevent。

 

Greenlet（greenlet的执行顺序需要我们手动控制）

复制代码
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from greenlet import greenlet
def test1():
    print (11)
    gr2.switch()    #手动切换
    print (22)
    gr2.switch()

def test2():
    print (33)
    gr1.switch()
    print (44)

gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()
复制代码
gevent（自动切换，由于切换是在IO操作时自动完成，所以gevent需要修改Python自带的一些标准库，这一过程在启动时通过monkey patch完成）

复制代码
from gevent import monkey; monkey.patch_all()
import gevent
import time


def foo():
    print('11')
    time.sleep(3)
    print('22')

def bar():
    print('33')
    print('44')

gevent.joinall([
    gevent.spawn(foo),
    gevent.spawn(bar),
])
复制代码
运行结果：（从结果可以看出，它们是并发执行的）

11
33
44
22