0
点赞
收藏
分享

微信扫一扫

农产品农货经营小程序商城的作用是什么

天使魔鬼 2023-10-21 阅读 32

进程和线程的比较

GIL全局解释器锁

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

背景信息:

以下几个问题是需要理解记忆的


# -计算密集型,消耗cpu,如果开了8个线程,第一个线程会一直占着cpu,而不会调度到其他线程执行,其他7个线程根本没执行,所以我们开8个进程,每个进程有一个线程,8个进程下的线程会被8个cpu执行,从而效率高


计算密集型选多进程好一些,在其他语言中,都是选择多线程,而不选择多进程.

 

互斥锁

在多线程的情况下,同时执行一个数据,会发生数据错乱的问题

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锁,为什么还要互斥锁? (多线程下)

 

线程队列

queue队列:使用import queue,用法与进程Queue一样

 

先进先出 

class queue.Queue(maxsize=0)
import queue

q=queue.Queue()
q.put('first')
q.put('second')
q.put('third')

print(q.get())
print(q.get())
print(q.get())
'''
结果(先进先出):
first
second
third
'''

后进先出

class queue.LifoQueue(maxsize=0)
import queue

q=queue.LifoQueue()
q.put('first')
q.put('second')
q.put('third')

print(q.get())
print(q.get())
print(q.get())
'''
结果(后进先出):
third
second
first
'''

 

优先级队列

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

进程池和线程池的使用

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


进程池和线程池有什么好处呢? 

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)

协程

一 猴子补丁

1.1 什么是猴子补丁?

1,这个词原来为Guerrilla Patch,杂牌军、游击队,说明这部分不是原装的,在英文里guerilla发音和gorllia(猩猩)相似,再后来就写了monkey(猴子)。

2,还有一种解释是说由于这种方式将原来的代码弄乱了(messing with it),在英文里叫monkeying about(顽皮的),所以叫做Monkey Patch。

名字听起来稀奇古怪的, 跟python的这个功能搭不上边, 所以我们直接来说功能吧!

 

1.2 猴子补丁的功能(一切皆对象)

1.拥有在模块运行时替换的功能, 例如: 一个函数对象赋值给另外一个函数对象(把函数原本的执行的功能给替换了)

class Monkey():
    def play(self):
        print('猴子在玩')

class Dog():
    def play(self):
        print('狗子在玩')
m=Monkey()
m.play()
m.play=Dog().play
m.play()

1.3 monkey patch的应用场景

这里有一个比较实用的例子,很多用到import json, 后来发现ujson性能更高,如果觉得把每个文件的import json改成import ujson as json成本较高, 或者说想测试一下ujson替换是否符合预期, 只需要在入口加上:

import json
import ujson

def monkey_patch_json():
    json.__name__ = 'ujson'
    json.dumps = ujson.dumps
    json.loads = ujson.loads
monkey_patch_json()
aa=json.dumps({'name':'lqz','age':19})
print(aa)

 

二 Gevent介绍

Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。 Greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度。

 

2.1 用法

#用法
g1=gevent.spawn(func,1,,2,3,x=4,y=5)创建一个协程对象g1,spawn括号内第一个参数是函数名,如eat,后面可以有多个参数,可以是位置实参或关键字实参,都是传给函数eat的

g2=gevent.spawn(func2)

g1.join() #等待g1结束

g2.join() #等待g2结束

#或者上述两步合作一步:gevent.joinall([g1,g2])

g1.value#拿到func1的返回值

 

2.2 示例1(遇到io自动切)

import gevent
def eat(name):
    print('%s eat 1' %name)
    gevent.sleep(2)
    print('%s eat 2' %name)

def play(name):
    print('%s play 1' %name)
    gevent.sleep(1)
    print('%s play 2' %name)


g1=gevent.spawn(eat,'lqz')
g2=gevent.spawn(play,name='lqz')
g1.join()
g2.join()
#或者gevent.joinall([g1,g2])
print('主')

 

2.3 示例2

'''
上例gevent.sleep(2)模拟的是gevent可以识别的io阻塞,

而time.sleep(2)或其他的阻塞,gevent是不能直接识别的需要用下面一行代码,打补丁,就可以识别了

from gevent import monkey;monkey.patch_all()必须放到被打补丁者的前面,如time,socket模块之前

或者我们干脆记忆成:要用gevent,需要将from gevent import monkey;monkey.patch_all()放到文件的开头
'''
from gevent import monkey;monkey.patch_all()

import gevent
import time
def eat():
    print('eat food 1')
    time.sleep(2)
    print('eat food 2')

def play():
    print('play 1')
    time.sleep(1)
    print('play 2')

g1=gevent.spawn(eat)
g2=gevent.spawn(play_phone)
gevent.joinall([g1,g2])
print('主')

# 我们可以用threading.current_thread().getName()来查看每个g1和g2,查看的结果为DummyThread-n,即假线程

 

2.4 单线程的套接字服务端并发

服务端

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(500):
    t=Thread(target=socket_client)
    t.start()
举报

相关推荐

0 条评论