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)

 

  

 

 

 

 

 

 

 

posted @ 2019-12-09 16:01  HEU葉孤城  阅读(134)  评论(0编辑  收藏  举报