CSIC_716_20191209【并发编程---GIL和协程】
GIL Global Interpreter Lock 全局解释锁
GIL对含IO的任务来说,会出现不能保证数据安全的情况。如下:
from threading import Thread from threading import Lock import time n = 100 def task1(): global n m = n time.sleep(0.1) n = m + 1 if __name__ == '__main__': lock = Lock() list1 = [] for line in range(5): t1 = Thread(target=task1) t1.start() list1.append(t1) for i in list1: t1.join() print(n)
打印的值为101,因为在线程遇到IO时,会被剥夺CPU执行权限,当IO结束时,不同线程的均取到最初的n值。
********************************************************************************************************************************
含有IO的涉及修改数据的任务,要加上线程互斥锁
from threading import Thread from threading import Lock import time n = 100 def task1(): global n lock.acquire() m = n time.sleep(1) n = m + 1 lock.release() if __name__ == '__main__': lock = Lock() list1 = [] for line in range(5): t1 = Thread(target=task1) t1.start() list1.append(t1) for i in list1: t1.join() print(n)
结果为105,加了锁,将并发编程串行。
********************************************************************************************************************************
GIL对不含IO的任务来说,可以保证数据安全的情况。如下:
n = 100 def task1(): global n m = n n = m + 1 if __name__ == '__main__': lock = Lock() list1 = [] for line in range(5): t1 = Thread(target=task1) t1.start() list1.append(t1) for i in list1: t1.join() print(n)
结果为105,全局锁起到了线程锁的效果。
********************************************************************************************************************************
协程
协程用于在单线程下实现并发。
协程对IO密集型很有用,感觉是为了最大化利用操作系统分配给进程的时间片。
协程是手动实现IO切换+保存状态,去欺骗操作系统,让操作系统误以为没有发生IO。
使用第三方模块 gevent
# _*_ coding: gbk _*_ # @Author: Wonder from gevent import monkey # monkey.patch_all 猴子补丁 from gevent import spawn # spawn() ,用于创建协程 from gevent import joinall # joinall[spawn1,spawn2,spawn3] import time monkey.patch_all() def task1(): print('start......') time.sleep(1) print('end......') def task2(): print('start......') time.sleep(1) print('end......') if __name__ == '__main__': sp1 = spawn(task1) sp2 = spawn(task2) joinall([sp1, sp2]) # 将join合并起来了,等到协程结束,再结束线程
monkey.patch_all( ),监听所有的任务是否有IO操作,并将IO转为gevent能识别的IO,一定要写在最前面,导入时就写。
spawn()提交协程 ,内部做了start()
joinall( [ sp1 , sp2 , ....] ) 将等待sp1,sp2协程结束
网络编程+并发编程 实例
SOCKET套接字通信,Server端用协程并发处理,Client端用线程并发访问。
SERVER 服务端
# _*_ coding: gbk _*_ # @Author: Wonder import socket from gevent import monkey from gevent import spawn monkey.patch_all() def server(ip, port): server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind((ip, port)) server.listen(5) while True: conn, addr = server.accept() spawn(run, conn) # 协程 def run(conn): while True: try: data = conn.recv(1024) if not data: break print(data.decode('utf-8')) conn.send('永不在线'.encode('utf-8')) except Exception as e: print(e) break conn.close() if __name__ == '__main__': p1 = spawn(server, '127.0.0.1', 9527) # 协程 p1.join()
Client 客户端
# _*_ coding: gbk _*_ # @Author: Wonder import socket from concurrent.futures import ThreadPoolExecutor from threading import current_thread def client(i): cliet = socket.socket(socket.AF_INET, socket.SOCK_STREAM) cliet.connect( ('127.0.0.1', 9527) ) while True: cliet.send(('NO_%s;线程号:%s' % (i, current_thread().getName())).encode('utf-8')) data = cliet.recv(1024) print(data.decode('utf-8')) if __name__ == '__main__': pool = ThreadPoolExecutor(5) for i in range(100): pool.submit(client, i)