学习Python的第30天

1.GIL全局解释器锁

1.1定义

Python在设计之初就考虑到要在主循环中,同时只有一个线程在执行。虽然 Python 解释器中可以“运行”多个线程,但在任意时刻只有一个线程在解释器中运行。

1.2背景信息

1. Python代码运行在解释器上嘛,有解释器来执行或者解释
2. Python解释器的种类:
    1、CPython  2、IPython 3、PyPy  4、Jython  5、IronPython
3. 当前市场使用的最多(95%)的解释器就是CPython解释器
4. GIL全局解释器锁是存在于CPython中
5. 结论是同一时刻只有一个线程在执行? 想避免的问题是,出现多个线程抢夺资源的情况
    比如:现在起一个线程,来回收垃圾数据,回收a=1这个变量,另外一个线程也要使用这个变量a,当垃圾回收线程还没没有把变量a回收完毕,另一个线程就来抢夺这个变量a使用。
    怎么避免的这个问题,那就是在Python这门语言设计之处,就直接在解释器上添加了一把锁,这把锁就是为了让统一时刻只有一个线程在执行,言外之意就是哪个线程想执行,就必须先拿到这把锁(GIL), 只有等到这个线程把GIL锁释放掉,别的线程才能拿到,然后具备了执行权限..

1.3理论记忆

1. python有GIL锁的原因,同一个进程下多个线程实际上同一时刻,只有一个线程在执行
2. 只有在python上开进程用的多,其他语言一般不开多进程,只开多线程就够了
3. cpython解释器开多线程不能利用多核优势,只有开多进程才能利用多核优势,其他语言不存在这个问题
4. 8核cpu电脑,充分利用起我这个8核,至少起8个线程,8条线程全是计算--->计算机cpu使用率是100%,
5. 如果不存在GIL锁,一个进程下,开启8个线程,它就能够充分利用cpu资源,跑满cpu
6. cpython解释器中好多代码,模块都是基于GIL锁机制写起来的,改不了了---》我们不能有8个核,但我现在只能用1核,----》开启多进程---》每个进程下开启的线程,可以被多个cpu调度执行
7. cpython解释器:io密集型使用多线程,计算密集型使用多进程
    # -io密集型,遇到io操作会切换cpu,假设你开了8个线程,8个线程都有io操作---》io操作不消耗cpu---》一段时间内看上去,其实8个线程都执行了, 选多线程好一些

2.互斥锁

n = 10
from threading import Lock
import time
def task(lock):
    lock.acquire()
    global n
    temp = n
    time.sleep(0.5)
    n = temp - 1
    lock.release()


"""拿时间换空间,空间换时间 时间复杂度"""
from threading import Thread

if __name__ == '__main__':
    tt = []
    lock=Lock()
    for i in range(10):
        t = Thread(target=task, args=(lock, ))
        t.start()
        tt.append(t)
    for j in tt:
        j.join()

    print("主", n)
    
    
面试题:既然有了GIL锁,为什么还要互斥锁? (多线程下)
	   比如:我起了2个线程,来执行a=a+1,a一开始是0
       1. 第一个线程来了,拿到a=0,开始执行a=a+1,这个时候结果a就是1了
       2. 第一个线程得到的结果1还没有赋值回去给a,这个时候,第二个线程来了,拿到的a是0,继续执行
    		a=a+1结果还是1
       3. 加了互斥锁,就能够解决多线程下操作同一个数据,发生错乱的问题

3.线程队列

同一个进程下多个线程数据是共享的
为什么先同一个进程下还会去使用队列呢
因为队列是
    管道 + 锁
所以用队列还是为了保证数据的安全
"""

线程队列:
	1. 先进先出
    2. 后进先出
    3. 优先级的队列
    
 from multiprocessing import Queue

"""线程队列"""
import queue
queue.Queue()

# queue.Queue 的缺点是它的实现涉及到多个锁和条件变量,因此可能会影响性能和内存效率。
import queue

q=queue.Queue() # 无限大、
q.put('first')
q.put('second')
q.put('third')
q.put('third')
q.put('third')
q.put('third')
q.put('third')

print(q.get())
print(q.get())
print(q.get())
 
## 后进先出
import queue

# Lifo:last in first out
q=queue.LifoQueue()
q.put('first')
q.put('second')
q.put('third')

print(q.get())
print(q.get())
print(q.get())

## 优先级队列
import queue

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')

4.进程池和线程池的使用 

池:池子、容器类型,可以盛放多个元素

进程池:提前定义好一个池子,然后,往这个池子里面添加进程,以后,只需要往这个进程池里面丢任务就行了,然后,有这个进程池里面的任意一个进程来执行任务

线程池:提前定义好一个池子,然后,往这个池子里面添加线程,以后,只需要往这个线程池里面丢任务就行了,然后,有这个线程池里面的任意一个线程来执行任务
	
进程池和线程池有什么好处呢? 

def task(n, m):
    return n+m

def task1():
    return {'username':'kevin', 'password':123}
"""开进程池"""
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor


def callback(res):
    print(res) # Future at 0x1ed5a5e5610 state=finished returned int>
    print(res.result()) # 3

def callback1(res):
    print(res) # Future at 0x1ed5a5e5610 state=finished returned int>
    print(res.result()) # {'username': 'kevin', 'password': 123}
    print(res.result().get('username'))
if __name__ == '__main__':
    pool=ProcessPoolExecutor(3) # 定义一个进程池,里面有3个进程
    ## 2. 往池子里面丢任务

    pool.submit(task, m=1, n=2).add_done_callback(callback)
    pool.submit(task1).add_done_callback(callback1)
    pool.shutdown()  # join + close
    print(123)

5. 多线程爬取网页

import requests

def get_page(url):
    res=requests.get(url)
    name=url.rsplit('/')[-1]+'.html'
    return {'name':name,'text':res.content}

def call_back(fut):
    print(fut.result()['name'])
    with open(fut.result()['name'],'wb') as f:
        f.write(fut.result()['text'])


if __name__ == '__main__':
    pool=ThreadPoolExecutor(2)
    urls=['http://www.baidu.com','http://www.cnblogs.com','http://www.taobao.com']
    for url in urls:
        pool.submit(get_page,url).add_done_callback(call_back)

6. 协程实现高并发

服务端:
from gevent import monkey;

monkey.patch_all()
import gevent
from socket import socket
# from multiprocessing import Process
from threading import Thread


def talk(conn):
    while True:
        try:
            data = conn.recv(1024)
            if len(data) == 0: break
            print(data)
            conn.send(data.upper())
        except Exception as e:
            print(e)
    conn.close()


def server(ip, port):
    server = socket()
    server.bind((ip, port))
    server.listen(5)
    while True:
        conn, addr = server.accept()
        # t=Process(target=talk,args=(conn,))
        # t=Thread(target=talk,args=(conn,))
        # t.start()
        gevent.spawn(talk, conn)


if __name__ == '__main__':
    g1 = gevent.spawn(server, '127.0.0.1', 8080)
    g1.join()

客户端:
	import socket
from threading import current_thread, Thread


def socket_client():
    cli = socket.socket()
    cli.connect(('127.0.0.1', 8080))
    while True:
        ss = '%s say hello' % current_thread().getName()
        cli.send(ss.encode('utf-8'))
        data = cli.recv(1024)
        print(data)


for i in range(5000):
    t = Thread(target=socket_client)
    t.start()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值