3月31日课后总结

发布时间 2023-03-31 21:12:17作者: 橘子熊何妨

3/31课后总结

死锁现象(了解)

"""
	死锁是指两个或者两个以上的进程互相抢占资源而导致互相等待的过程
	单进程和单线程不会出现死锁
"""
import time
from threading import Thread, Lock, RLock  # RLock就是递归锁

"""
	连续赋值下两把锁其实是同一把锁,如果是普通锁会报错,因为普通的锁只能锁一次
	递归锁不会,递归锁可以多次上锁,但是释放的时候锁了多少次就得释放多少次
	如果是用的两把锁,哪怕是递归锁还是会发生死锁
"""

# noodle_lock = fork_lock = Lock()  
# noodle_lock = Lock()
# fork_lock = Lock()
noodle_lock = fork_lock= RLock()  # 递归锁,也叫可重入锁
# noodle_lock = RLock()
# fork_lock = RLock()
print(noodle_lock)
print(fork_lock)

def eat1(name):
    noodle_lock.acquire()
    print('%s 抢到了面条' % name)
    fork_lock.acquire()
    print('%s 抢到了叉子' % name)
    print('%s 吃面' % name)
    fork_lock.release()
    noodle_lock.release()

def eat2(name):
    fork_lock.acquire()
    print('%s 抢到了叉子' % name)
    time.sleep(1)
    noodle_lock.acquire()
    print('%s 抢到了面条' % name)
    print('%s 吃面' % name)
    noodle_lock.release()
    fork_lock.release()

for name in ['哪吒', 'jack', 'tank']:
    t1 = Thread(target=eat1, args=(name,))
    t2 = Thread(target=eat2, args=(name,))
    t1.start()
    t2.start()

线程队列

"""
	用一个进程下数据是共享的
	之所以在同一个进程下还继续使用队列是因为队列是管道+锁
	数据更加安全

	进程Queue用于父进程与子进程(或同一父进程中多个子进程)间数据传递
    python自己的多个进程间交换数据或者与其他语言(如Java)进程queue就无能为力
    queue.Queue 的缺点是它的实现涉及到多个锁和条件变量,因此可能会影响性能和内存效率。
"""

from threading import Thread
# from multiprocessing import Queue  # 这是进程的队列
import queue  # 线程的队列


# q = queue.Queue(3)  # 先进先出
# q = queue.LifoQueue(3)  # 先进后出
q = queue.PriorityQueue(3)  # 优先级队列

q.put((1,5))  # 元组里面的第一个数是优先级,小的先出
q.put((2,4))
q.put((3,3))

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

进程池和线程池(掌握思路)

"""
	池:盛放更多个对象的,盛放更多的进程和线程
	进程池和线程池都是异步调用,只需要往池子丢任务,等到任务完成后,内部会自动回调,告诉我们执行后的结果
	进程池就是创建一个池子,这个池里先创建好一堆的进程,然后往池子里丢任务就行了
	线程池类似
	创建池的目的是为了节省资源,防止内存被占满,另外就是可以提升效率,不建池子可能会无限开进程。建池子可以规定好池子的大小有效预防
"""
import time
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor

def test1():
    return 1+1

def test2():
    return 2+2

def test3():
    return 3+3

def callback(res):
    print(res)
    print(res.result())

if __name__ == '__main__':
    now_time = time.time()
    # p_pool = ProcessPoolExecutor(2)  # 创建一个进程池容量为2
    p_pool = ThreadPoolExecutor(2)  # 创建一个线程池容量为2
    p_pool.submit(test3).add_done_callback(callback)  # 执行任务,往池子丢任务,然后调用回调函数callback
    p_pool.submit(test1).add_done_callback(callback)
    p_pool.submit(test2).add_done_callback(callback)
    print(time.time()-now_time)

线程池爬取网页

import requests
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor


def get_page(url):
    # requests.get('http://www.baidu.com')
    res = requests.get(url)  # 爬取网页
    name = url.rsplit('/')[-1] + '.html'  # www.baidu.com.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(5)
    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)

协程

"""
    进程:资源分配的最小单位
    线程:CPU执行的最小单位
    协程:不存在于操作系统中,是程序员创造的工具,被称之为用户态的轻量级线程,是单线程下的并发
    并发:切换,保存状态
    协程的本质还是来回切换,只不过是程序级别的切换
    想要使用协程需要借助第三方模块gevent模块(需要下载)
"""

协程实现高并发

import socket
from gevent import spawn,monkey
monkey.patch_all()

lis = []

def run(conn,client_addr):

    while True:
        try:
            data = conn.recv(1024)
            data = data.decode('utf-8')
            if data in lis:
                conn.send('关闭该次链接'.encode('utf-8'))
                break
                # continue
            print(f'接受到了来自{client_addr[0]}端口{client_addr[1]}传的{data}')
            lis.append(data)
            conn.send('接受到了'.encode('utf-8'))
        except Exception as e:
            print(e)
            break
    conn.close()

def begin():
    server = socket.socket()
    server.bind(('192.168.1.171', 9000))
    server.listen(5)
    while True:
        conn, client_addr = server.accept()
        spawn(run,conn,client_addr)

x = spawn(begin)
x.join()

客户端

import socket
from threading import Thread,current_thread

def client():
    client = socket.socket()
    client.connect(('192.168.1.171', 9000))

    while True:
        try:
            client.send(f'开启了{current_thread().name}'.encode('utf-8'))
            server_data = client.recv(1024)
            data = server_data.decode('utf-8')
            if data == '关闭该次链接':
                break
            print(data,current_thread().name)
        except Exception:
            break
    client.close()

if __name__ == '__main__':
    for i in range(500):
        t = Thread(target=client)
        t.start()