1.线程回调


回调:
在线程池/进程池
每次提交任务 都会返回一个表示任务的对象 Future对象
Future对象具备一个绑定方法  add_done_callback 用于指定回调函数
add 意味着可以添加多个回调函数

如果直接使用Thread的话 如何完成回调:

from threading import Thread

import time

# res = None


def call_back(res):
    print("任务结果拿到了:%s" % res)

def parser(res):
    print("任务结果拿到了:%s" % res)

def task(callback):
    # global res
    print("run")
    time.sleep(1)
    #     # return 100
    res = 100 # 表示任务结果
    callback(res) # 执行回调函数 并传入任务结果



t = Thread(target=task,args=(parser,))
t.start()
# t.join()
# print(res)
print("over")



2.线程中的队列



from queue import Queue,LifoQueue,PriorityQueue

1.Queue() 先进先出
# 与进程中的Joinablequeue  使用方式一模一样  但是 不具备IPC
# q = Queue()
#
# q.put("123")
# q.put("456")
#
# print(q.get())
# print(q.get())
#
# # print(q.get(block=True,timeout=3))
# q.task_done()
# q.task_done()
# q.join()
# print("over")

2.LifoQueue
last in first out 后进先出   先进 后出   模拟堆栈
# 除顺序以外别的都一样
# lq = LifoQueue()
#
# lq.put("123")
# lq.put("456")
#
# print(lq.get())
# print(lq.get())

3. PriorityQueue 
具备优先级的队列
# 可以存储一个可以比较大小的对象    比较越小的优先级越高
    自定义对象 不能使用比较运算符  所以不能存储

q=queue.PriorityQueue()
#put进入一个元组,元组的第一个元素是优先级(通常是数字,也可
以是非数字之间的比较),数字越小优先级越高
q.put((20,'a'))
q.put((10,'b'))
q.put((30,'c'))

print(q.get())
print(q.get())
print(q.get())
'''
结果(数字越小优先级越高,优先级高的优先出队):
(10, 'b')
(20, 'a')
(30, 'c')



3.事件EVENT



事件Event:
事件,表示发生了某件事情,我们可以去关注某个事件然后采取一
些行动  
本质上事件是用来线程间通讯的 ,用于状态同步  

from threading import Thread,Event

import time

boot_event = Event()

# boot_event.clear() 回复事件的状态为False
# boot_event.is_set() 返回事件的状态
# boot_event.wait()等待事件发生 ,就是等待事件被设置
为True
# boot_event.set() 设置事件为True



def boot_server():
    print("正在启动服务器......")
    time.sleep(3)
    print("服务器启动成功!")
    boot_event.set() # 标记事件已经发生了


def connect_server():
    boot_event.wait() # 等待事件发生
    print("链接服务器成功!")

t1 = Thread(target=boot_server)
t1.start()

t2 = Thread(target=connect_server)
t2.start()



4.协程



协程的目的就是要在单线程中实现并发
并发:
多个任务看起来是同时运行 本质是切换+保存状态 
1.生成器中yiled实现并发效果
# 使用生成器来实现 单线 并发多个任务
import time
# def func1():
#     a = 1
#     for i in range(10000000):
#         a += 1
#         # print("a run")
#         yield
#
# def func2():
#     res = func1()
#     a = 1
#     for i in range(10000000):
#         a += 1
#         # print("b run")
#         next(res)
#
# st = time.time()
# func2()
# print(time.time() - st)

def func1():
    a = 1
    for i in range(10000000):
        a += 1

def func2():
    a = 1
    for i in range(10000000):
        a += 1


st = time.time()
func1()
func2()
print(time.time() - st)
经过测试 单线程并发并不能提高性能 , 对于计算密集型
任务而言  

对于IO操作而言 必须具备能够检测io操作 并自动切换其他
区任务  ,这才提高效率



2.greenlet
greenlet需要手动切换,而且不能 检测IO ,是对yie
ld的封装
import greenlet

import time


def task1():
    print("task1 run")
    g2.switch()
    print("task1 over")
    g2.switch()


def task2():
    print("task2 run")
    g1.switch()
    time.sleep(2)
    print("task2 over")

g1 = greenlet.greenlet(task1)
g2 = greenlet.greenlet(task2)

g1.switch()
# g2.switch()

print("主 over")



3.gevent
gevent  是协程 ,为轻量级线程 ,也称之为微线程,
是应用程序级别的任务调度方式 

提高效率:
在Cpython中有GIL锁 导致多线程不能并行执行丧失了 多
核优势,即使开启了多线程也只能并发, 这时候完全 可以
使用协程来实现并发
协程的优缺点:
优点:不会占用更多无用的资源 
缺点:如果是计算任务  使用协程反而降低效率   
IO密集型任务   采用协程



5.猴子补丁



本质就是把原本阻塞的代码 悄悄换成非阻塞代码  

例如 Queue.get(block=false)
当执行get而取不到值时 会抛出异常  只需捕获异常  然后
在发生时切换到其他任务 ,就可以实现遇到IO 切换任务 
# gevent 不具备检测IO的能力  需要为它打补丁  打上补
丁之后就能检测IO
# 注意补丁一定打在最上面    必须保证导入模块前就打好补丁
from gevent import monkey
monkey.patch_all()   #

from threading import current_thread
import gevent,time


def task1():
    print(current_thread(),1)
    print("task1 run")
    # gevent.sleep(3)
    time.sleep(3)
    print("task1 over")

def task2():
    print(current_thread(),2)
    print("task2 run")
    print("task2 over")

# spawn 用于创建一个协程任务
g1 = gevent.spawn(task1)
g2 = gevent.spawn(task2)

# 任务要执行,必须保证主线程没挂  因为所有协程任务
都是主线在执行   ,必须调用join来等待协程任务
# g1.join()
# g2.join()
# 理论上等待执行时间最长的任务就行 , 但是不清楚
谁的时间长 可以全部join

gevent.joinall([g1,g2])
print("over")



协程: 
在Cpython 多线程无法并行 执行 ,在IO密集任务中 
 并且并发量较大,无法开启更多的线程时,造成后续的任
务无法处理,即使前面都在等待IO

就可以使用单线程 实现并发,当某一个任务处于IO阻塞时
,可以切换到其他的任务来执行,可以充分利用CPU时间片
,如果任务量足够,可以占用CPU直到超时

最终的解决方案:多进程 +单线程 + 协程 

对于更多的任务:
1.集群    所有服务器干的活都一样
2.分布式   每个服务器就干某个活