高中信息技术python编程,高中信息技术python编程例题

前言

回顾昨天的内容,昨天从基础入门,列表与元组,字符串,字典,条件循环和其他语句,函数,面向对象编程,异常与文件处理等八个方向讲述了python语法编程,今天来到第二章

python并发编程文档+笔记+了解接单+源码获取方式

从以下四个方向展开讲述

  • 网络编程
  • 线程
  • 多进程
  • 协程

第一章:网络编程

1.初网络编程

网络编程是指使用计算机网络进行通信的编程技术。在Python中,可以使用socket模块来实现网络编程。

socket是一个封装了TCP/IP协议的网络编程库,它提供了一种通用的网络编程接口,可以用于创建客户端和服务器端程序。在Python中,可以使用socket模块来创建socket对象,然后使用该对象进行网络通信

下面是一个简单的网络编程示例,其中创建了一个服务器端和一个客户端,客户端向服务器端发送消息,服务器端接收到消息后将其打印出来:

服务器端代码:

import socket

# 创建socket对象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 绑定IP地址和端口号
server_socket.bind(('127.0.0.1', 8888))

# 监听端口
server_socket.listen(5)

print('服务器已启动,等待客户端连接...')

# 等待客户端连接
client_socket, client_address = server_socket.accept()

print('客户端已连接,地址为:', client_address)

# 接收客户端消息
data = client_socket.recv(1024)

print('接收到客户端消息:', data.decode())

# 关闭socket连接
client_socket.close()
server_socket.close()

客户端代码:

import socket

# 创建socket对象
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 连接服务器
client_socket.connect(('127.0.0.1', 8888))

# 发送消息
client_socket.send('Hello, server!'.encode())

# 关闭socket连接
client_socket.close()

PyCharm中编译和运行Matlab文件,需要先安装Matlab并配置好环境变量。然后,在PyCharm中打开Matlab文件,点击运行按钮即可编译和运行Matlab文件。如果需要传递参数,可以在运行配置中设置。

2.TCP/IP简介

TCP/IP是一种网络协议,它是互联网的基础协议。TCP/IP协议族包括了许多协议,其中最重要的是TCPIP协议。

TCP协议是一种面向连接的协议,它提供了可靠的数据传输服务。TCP协议通过三次握手建立连接,然后通过数据分段和确认机制来保证数据的可靠传输。

IP协议是一种无连接的协议,它提供了数据包的传输服务。IP协议通过路由选择算法来确定数据包的传输路径,然后将数据包传输到目的地。

在网络编程中,我们通常使用TCP协议来进行数据传输。TCP协议提供了可靠的数据传输服务,适用于需要保证数据传输可靠性的场景,如文件传输、邮件传输等。而IP协议则适用于需要快速传输数据的场景,如视频流传输、实时通信等。

3.网络设计模块

1.Socket简介

Socket是一种通信机制,它允许不同的进程在网络上进行通信。在Python中,Socket是通过socket模块来实现的。Socket通常用于客户端和服务器之间的通信,但也可以用于进程之间的通信。

2.Socket模块使用

Python中的socket模块提供了一组函数和类,用于创建和操作Socket。常用的函数和类包括:

  • socket():创建一个Socket对象。
  • bind():将Socket绑定到一个特定的地址和端口。
  • listen():开始监听连接请求。
  • accept():接受一个连接请求,并返回一个新的Socket对象。
  • connect():连接到一个远程Socket。
  • send():发送数据。
  • recv():接收数据。

3.服务器

在Python中,可以使用socket模块创建一个服务器。服务器通常需要绑定到一个特定的地址和端口,并监听连接请求。当有客户端连接到服务器时,服务器会接受连接请求,并创建一个新的Socket对象来处理客户端请求。

以下是一个简单的Python服务器示例:

import socket

HOST = '127.0.0.1'  # 服务器地址
PORT = 8888  # 服务器端口号

# 创建一个Socket对象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 绑定到地址和端口
server_socket.bind((HOST, PORT))

# 开始监听连接请求
server_socket.listen(1)

print('Server is running on {}:{}'.format(HOST, PORT))

while True:
    # 接受一个连接请求,并返回一个新的Socket对象
    client_socket, client_address = server_socket.accept()
    print('Client connected from {}:{}'.format(client_address[0], client_address[1]))

    # 处理客户端请求
    data = client_socket.recv(1024)
    print('Received data: {}'.format(data.decode()))

    # 发送响应数据
    response = 'Hello, client!'
    client_socket.send(response.encode())

    # 关闭连接
    client_socket.close()

4.客户端

在Python中,可以使用socket模块创建一个客户端。客户端通常需要连接到一个远程Socket,并发送请求数据。当服务器响应请求时,客户端会接收响应数据。

以下是一个简单的Python客户端示例:

import socket

HOST = '127.0.0.1'  # 服务器地址
PORT = 8888  # 服务器端口号

# 创建一个Socket对象
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 连接到远程Socket
client_socket.connect((HOST, PORT))

# 发送请求数据
request = 'Hello, server!'
client_socket.send(request.encode())

# 接收响应数据
response = client_socket.recv(1024)
print('Received data: {}'.format(response.decode()))

# 关闭连接
client_socket.close()

5.文件下载器

文件下载器是一个常见的网络应用程序,它可以从远程服务器下载文件并保存到本地。在Python中,可以使用socket模块和urllib模块来实现文件下载器。

以下是一个简单的Python文件下载器示例:

import socket
import urllib.request

HOST = '127.0.0.1'  # 服务器地址
PORT = 8888  # 服务器端口号
FILE_URL = 'http://example.com/file.txt'  # 文件下载地址
FILE_NAME = 'file.txt'  # 文件保存路径

# 创建一个Socket对象
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 连接到远程Socket
client_socket.connect((HOST, PORT))

# 发送请求数据
request = 'GET {}\r\n'.format(FILE_URL)
client_socket.send(request.encode())

# 接收响应数据
response = client_socket.recv(1024)
print('Received data: {}'.format(response.decode()))

# 下载文件并保存到本地
with open(FILE_NAME, 'wb') as f:
    while True:
        data = client_socket.recv(1024)
        if not data:
            break
        f.write(data)

# 关闭连接
client_socket.close()

4.补充内容

1.网络编程中的UDP协议

UDP(User Datagram Protocol)是一种无连接的、不可靠的传输协议,它不保证数据包的可靠性和顺序性,但是传输速度快,适用于一些对数据可靠性要求不高的应用场景,如视频、音频等实时传输。

在Python中,使用socket模块可以实现UDP协议的网络编程。下面是一个简单的UDP服务器和客户端的示例:

UDP服务器:

import socket

# 创建UDP套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 绑定IP地址和端口号
server_socket.bind(('127.0.0.1', 8888))

while True:
    # 接收数据
    data, addr = server_socket.recvfrom(1024)
    print('Received from %s:%s.' % addr)
    print('Data: %s' % data.decode())

    # 发送数据
    server_socket.sendto('Hello, client!'.encode(), addr)

UDP客户端:

python
import socket

# 创建UDP套接字
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 发送数据
client_socket.sendto('Hello, server!'.encode(), ('127.0.0.1', 8888))

# 接收数据
data, addr = client_socket.recvfrom(1024)
print('Received from %s:%s.' % addr)
print('Data: %s' % data.decode())

# 关闭套接字
client_socket.close()

UDP协议中,发送数据时需要指定目标地址和端口号,接收数据时会返回发送方的地址和端口号。由于UDP协议不保证数据的可靠性和顺序性,因此在实际应用中需要考虑数据丢失、重复、乱序等问题。

2.UDP协议与TCP协议的区别

UDP协议和TCP协议是两种常用的网络传输协议,它们有以下几点区别:

  • 连接方式TCP协议是面向连接的协议,而UDP协议是无连接的协议。TCP协议在传输数据之前需要先建立连接,而UDP协议不需要。
  • 可靠性TCP协议是可靠的协议,它保证数据的可靠传输,而UDP协议是不可靠的协议,它不保证数据的可靠传输。
  • 传输效率UDP协议比TCP协议传输效率高,因为UDP协议不需要建立连接和维护连接状态,而TCP协议需要。
  • 数据包大小UDP协议传输的数据包大小限制为64KB,而TCP协议没有限制。
  • 应用场景TCP协议适用于对数据传输可靠性要求较高的场景,如文件传输、邮件传输等;而UDP协议适用于对数据传输实时性要求较高的场景,如视频直播、语音通话等。

3.UDP协议代码实现方式

UDP协议是一种无连接的协议,它不保证数据传输的可靠性和顺序性,但是具有传输速度快的优点。下面是Python中使用UDP协议进行网络编程的代码实现方式:

服务器端代码:

import socket

# 创建UDP套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 绑定IP地址和端口号
server_socket.bind(('127.0.0.1', 8888))

# 接收数据
while True:
    data, addr = server_socket.recvfrom(1024)
    print('Received from %s:%s.' % addr)
    print('Data: %s' % data.decode())

    # 发送数据
    server_socket.sendto('Hello, client!'.encode(), addr)

客户端代码:

import socket

# 创建UDP套接字
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 发送数据
client_socket.sendto('Hello, server!'.encode(), ('127.0.0.1', 8888))

# 接收数据
data, addr = client_socket.recvfrom(1024)
print('Received from %s:%s.' % addr)
print('Data: %s' % data.decode())

# 关闭套接字
client_socket.close()

在上面的代码中,服务器端首先创建了一个UDP套接字,并绑定了IP地址和端口号。然后通过recvfrom()方法接收客户端发送的数据,并通过sendto()方法向客户端发送数据。客户端同样创建了一个UDP套接字,并通过sendto()方法向服务器端发送数据,然后通过recvfrom()方法接收服务器端发送的数据。最后,客户端关闭套接字。

第二章:多线程

1.线程和进程

线程和进程是操作系统中的两个重要概念,它们都是并发编程的基础。线程是操作系统能够进行运算调度的最小单位,而进程则是操作系统进行资源分配和调度的基本单位。

线程和进程的区别:

  • 线程是进程的一部分,一个进程可以包含多个线程,而一个线程只能属于一个进程。
  • 进程拥有独立的内存空间,而线程共享进程的内存空间。
  • 进程之间的通信需要使用IPC(Inter-Process Communication)机制,而线程之间可以直接共享数据。
  • 进程的创建和销毁比线程慢,因为进程需要分配和释放独立的内存空间,而线程只需要分配和释放一些寄存器和栈空间。

在Python中,可以使用threading模块来创建和管理线程。下面是一个简单的线程示例:

import threading

def worker():
    print('Worker thread started')
    # do some work here
    print('Worker thread finished')

# create a new thread
t = threading.Thread(target=worker)
# start the thread
t.start()
# wait for the thread to finish
t.join()

在这个示例中,我们创建了一个名为worker的函数,它将在一个新的线程中运行。我们使用threading.Thread类创建了一个新的线程对象,并将worker函数作为目标传递给它。然后,我们使用start()方法启动线程,并使用join()方法等待线程完成。

2.使用线程

在Python中,使用线程可以通过threading模块来实现。下面是一个简单的例子,展示了如何使用线程:

import threading

def worker():
    """线程执行的任务"""
    print("Worker thread started")
    # 执行一些任务
    print("Worker thread finished")

# 创建线程
t = threading.Thread(target=worker)
# 启动线程
t.start()

# 主线程继续执行其他任务
print("Main thread finished")

在上面的例子中,我们首先定义了一个worker函数,它将在一个单独的线程中执行。然后,我们使用threading.Thread类创建了一个新的线程,并将worker函数作为参数传递给它。最后,我们调用start方法来启动线程。

注意,线程是异步执行的,因此主线程不会等待线程完成。在上面的例子中,主线程会立即继续执行,输出Main thread finished。如果我们希望等待线程完成后再继续执行主线程,可以使用join方法:

# 等待线程完成
t.join()


# 主线程继续执行其他任务
print("Main thread finished")

在上面的代码中,我们在启动线程后调用了t.join()方法,这将阻塞主线程,直到线程完成。然后,主线程才会继续执行。

3.多线程全局变量

在多线程编程中,多个线程可以共享全局变量。但是需要注意的是,多个线程同时对同一个全局变量进行读写操作时,可能会出现数据竞争(Data Race)的问题,导致程序出现不可预期的结果。

为了避免数据竞争,可以使用线程锁(Thread lock)来保证同一时刻只有一个线程可以访问共享变量。Python中提供了LockRLockSemaphore等多种锁机制,可以根据实际需求选择合适的锁。

下面是一个使用Lock来保证多线程共享全局变量安全的示例代码:

import threading

# 定义全局变量
count = 0

# 定义线程锁
lock = threading.Lock()

# 定义线程函数
def add():
    global count
    for i in range(100000):
        # 获取锁
        lock.acquire()
        count += 1
        # 释放锁
        lock.release()

# 创建两个线程
t1 = threading.Thread(target=add)
t2 = threading.Thread(target=add)

# 启动线程
t1.start()
t2.start()

# 等待线程执行完毕
t1.join()
t2.join()

# 输出结果
print(count)

在上面的代码中,我们定义了一个全局变量count,并使用Lock来保证多个线程对count进行读写操作时的安全性。在每个线程中,我们首先获取锁,然后对count进行加1操作,最后释放锁。这样就可以保证同一时刻只有一个线程可以访问count,避免了数据竞争的问题。

需要注意的是,使用锁会带来一定的性能损失,因为每次获取锁和释放锁都需要一定的时间。因此,在实际应用中,需要根据实际情况来选择合适的锁机制,避免过度使用锁导致程序性能下降。

4.共享全局变量所带来的问题

在多线程编程中,多个线程可以共享全局变量。但是,共享全局变量也会带来一些问题:

  • 竞争条件:当多个线程同时访问和修改同一个全局变量时,可能会出现竞争条件,导致程序出现不可预测的结果。
  • 数据不一致:当多个线程同时修改同一个全局变量时,可能会导致数据不一致的问题,即某些线程看到的变量值与其他线程看到的不同。
  • 死锁:当多个线程同时等待对方释放某个资源时,可能会出现死锁的情况,导致程序无法继续执行。

因此,在多线程编程中,需要注意对共享全局变量的访问和修改,避免出现上述问题。可以使用锁、条件变量等机制来保证线程之间的同步和互斥。

5.解决线程同时修改全局变量的方式

在多线程编程中,共享全局变量可能会带来一些问题,例如:

  • 竞争条件:多个线程同时修改同一个全局变量,可能会导致数据不一致或者出现意料之外的结果。
  • 死锁:多个线程同时等待对方释放资源,导致程序无法继续执行。

为了解决这些问题,可以采用以下方式:

  • 使用锁:在访问共享变量时,使用锁来保证同一时刻只有一个线程可以修改变量。Python中提供了threading模块中的Lock类来实现锁。
  • 使用线程安全的数据结构:Python中提供了一些线程安全的数据结构,例如Queue、deque等,可以在多线程环境下安全地访问和修改数据。
  • 使用局部变量:将全局变量作为参数传递给线程函数,让线程函数在局部变量上进行操作,避免多个线程同时修改同一个全局变量。
  • 使用线程本地存储:Python中提供了threading模块中的local类,可以在每个线程中创建一个独立的变量,避免多个线程之间共享变量。

6.互斥锁

在多线程编程中,互斥锁是一种常用的同步机制,用于保护共享资源,防止多个线程同时修改同一个变量导致数据不一致的问题。

互斥锁的基本思想是,在访问共享资源之前,先获取锁,如果锁已经被其他线程获取,则当前线程会被阻塞,直到锁被释放为止。在访问完共享资源之后,释放锁,让其他线程可以获取锁并访问共享资源。

在Python中,可以使用threading模块中的Lock类来实现互斥锁。Lock类有两个基本方法:

  • acquire([blocking]):获取锁,如果锁已经被其他线程获取,则当前线程会被阻塞。如果blockingFalse,则获取锁失败时会立即返回False,而不是阻塞等待。
  • release():释放锁,让其他线程可以获取锁并访问共享资源。 下面是一个使用互斥锁的例子:
import threading

# 共享变量
count = 0

# 创建互斥锁
lock = threading.Lock()

# 线程函数
def worker():
    global count
    for i in range(100000):
        # 获取锁
        lock.acquire()
        try:
            count += 1
        finally:
            # 释放锁
            lock.release()

# 创建多个线程
threads = []
for i in range(10):
    t = threading.Thread(target=worker)
    threads.append(t)

# 启动线程
for t in threads:
    t.start()

# 等待所有线程执行完毕
for t in threads:
    t.join()

# 输出结果
print(count)

在上面的例子中,我们创建了一个共享变量count,并使用互斥锁来保护它。在每个线程中,我们先获取锁,然后修改count的值,最后释放锁。这样就可以保证多个线程不会同时修改count的值,从而避免了数据不一致的问题。最后输出count的值,可以看到它的值为1000000,符合预期。

7.死锁

死锁是指两个或多个线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力干涉那它们都将无法继续执行下去。在多线程编程中,死锁是一种常见的问题,需要特别注意。

死锁的产生通常需要满足以下四个条件

  • 互斥条件:一个资源每次只能被一个线程使用。
  • 请求与保持条件:一个线程因请求资源而阻塞时,对已获得的资源保持不放。
  • 不剥夺条件:线程已获得的资源,在未使用完之前,不能被其他线程强行剥夺,只能由该线程自己释放。
  • 循环等待条件:若干线程之间形成一种头尾相接的循环等待资源的关系。

为了避免死锁的产生,可以采用以下几种方式

  • 避免使用多个锁,尽量使用一个锁来控制多个资源的访问。
  • 避免持有锁的时间过长,尽量缩短锁的持有时间。
  • 避免循环等待,尽量按照固定的顺序获取锁。
  • 使用超时机制,当等待时间超过一定时间后,自动释放锁,避免长时间等待造成的死锁。

8.线程池

线程池是一种线程管理技术,它可以在程序启动时创建一定数量的线程,放入一个池中,当需要使用线程时,就从池中取出一个线程执行任务,任务执行完毕后,线程并不会被销毁,而是放回池中等待下一次任务的到来。

使用线程池可以避免频繁创建和销毁线程的开销,提高程序的性能和效率。在Python中,可以使用标准库中的concurrent.futures模块来实现线程池。

下面是一个简单的线程池示例:

import concurrent.futures
import time

def worker(num):
    print(f"Thread-{num} started")
    time.sleep(1)
    print(f"Thread-{num} finished")

if __name__ == '__main__':
    with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
        for i in range(5):
            executor.submit(worker, i)

在这个示例中,我们创建了一个包含3个线程的线程池,然后提交了5个任务给线程池执行。由于线程池中只有3个线程,因此只有3个任务会同时执行,其余的任务会等待空闲线程的出现。

输出结果如下:

Thread-0 started
Thread-1 started
Thread-2 started
Thread-0 finished
Thread-3 started
Thread-1 finished
Thread-4 started
Thread-2 finished
Thread-3 finished
Thread-4 finished

可以看到,线程池中的3个线程依次执行了5个任务,任务的执行顺序与提交的顺序无关。

第三章:多进程

1.进程的状态

在操作系统中,进程有以下几种状态

  • 就绪状态(Ready):进程已经准备好运行,等待分配CPU时间片。
  • 运行状态(Running):进程正在运行,占用CPU时间片。
  • 阻塞状态(Blocked):进程因为某些原因无法继续执行,例如等待I/O操作完成或等待某个资源的释放。
  • 挂起状态(Suspended):进程被暂停,不再占用CPU时间片,但是它的状态信息仍然保存在内存中。
  • 终止状态(Terminated):进程已经完成执行或被强制终止。

进程的状态转换通常是由操作系统内核进行控制的,例如当一个进程等待I/O操作完成时,操作系统会将该进程的状态从就绪状态转换为阻塞状态,当I/O操作完成后,操作系统会将该进程的状态从阻塞状态转换为就绪状态,等待分配CPU时间片。

2.线程的创建-multiprocessing

在 Python 中,可以使用 multiprocessing 模块来创建多进程。multiprocessing 模块提供了一个 Process 类,可以用来创建进程。下面是一个简单的例子:

import multiprocessing

def worker():
    """子进程要执行的任务"""
    print('Worker')

if __name__ == '__main__':
    # 创建子进程
    p = multiprocessing.Process(target=worker)
    # 启动子进程
    p.start()
    # 等待子进程结束
    p.join()

在上面的例子中,我们首先定义了一个 worker 函数,它是子进程要执行的任务。然后,我们使用 multiprocessing.Process 类创建了一个子进程,并将 worker 函数作为参数传递给了 Process 类的构造函数。接着,我们调用 start 方法启动子进程,最后调用 join 方法等待子进程结束。

需要注意的是,在 Windows 系统中,由于 multiprocessing 模块使用了 fork 系统调用,而 Windows 不支持 fork,因此需要在 if __name__ == '__main__': 语句中调用子进程的代码。这是因为在 Windows 中,每个进程都会执行一遍程序的所有代码,而 if __name__ == '__main__': 语句可以保证子进程只会执行指定的代码。

3.进程丶线程对比

进程和线程都是实现并发编程的方式,但是它们有以下不同点:

  • 资源占用:进程拥有独立的内存空间,而线程共享进程的内存空间。因此,创建进程的开销比创建线程大,同时进程间的通信也比线程间的通信复杂。
  • 并发性:由于线程共享进程的内存空间,因此线程间的通信和数据共享比进程间的通信和数据共享更容易。同时,线程的切换比进程的切换更快,因此线程的并发性比进程高。
  • 安全性:由于线程共享进程的内存空间,因此多个线程同时访问同一块内存时,可能会出现竞争条件,导致数据不一致或者程序崩溃。而进程之间的内存空间是独立的,因此进程间的数据不会相互影响。
  • 编程复杂度:由于进程间的通信和数据共享比较复杂,因此编写多进程程序的复杂度比编写多线程程序的复杂度高。

总的来说,进程适合于CPU密集型任务,而线程适合于IO密集型任务。在实际应用中,需要根据具体的场景选择合适的并发编程方式。

4.进程间的通信-Queue

在多进程编程中,不同进程之间的数据是无法直接共享的,因为每个进程都有自己独立的内存空间。因此,为了实现进程间的通信,我们需要使用一些特殊的机制。

其中,最常用的进程间通信方式是使用队列(Queue)。队列是一种先进先出(FIFO)的数据结构,可以用来在多个进程之间传递数据。

在Python中,我们可以使用multiprocessing模块中的Queue类来实现进程间通信。Queue类提供了put()get()方法,用于向队列中添加数据和从队列中取出数据。

下面是一个简单的例子,演示了如何使用Queue实现进程间通信:

from multiprocessing import Process, Queue

def worker(q):
    while True:
        item = q.get()
        if item is None:
            break
        print(item)

if __name__ == '__main__':
    q = Queue()
    p = Process(target=worker, args=(q,))
    p.start()

    for i in range(10):
        q.put(i)

    q.put(None)
    p.join()

在这个例子中,我们创建了一个进程p,它的工作是从队列q中取出数据并打印出来。主进程向队列中添加了10个数据,然后再添加一个None,表示数据已经全部添加完毕。最后,主进程等待进程p执行完毕。

需要注意的是,当我们向队列中添加数据时,如果队列已满,put()方法会阻塞,直到队列中有空闲位置。同样地,当我们从队列中取出数据时,如果队列为空,get()方法也会阻塞,直到队列中有数据可取。

除了Queue之外,Python中还提供了一些其他的进程间通信方式,比如PipeValueArray等。这些方式各有特点,可以根据具体的需求选择合适的方式

5.进程池的创建-pool

在Python中,我们可以使用multiprocessing模块中的Pool类来创建进程池。进程池是一组可重用的进程,可以在需要时分配给任务。这样可以避免频繁地创建和销毁进程,从而提高程序的效率。

下面是一个使用进程池的例子:

import multiprocessing

def worker(num):
    """进程池中的任务"""
    print('Worker %d is running' % num)

if __name__ == '__main__':
    # 创建进程池,池中有3个进程
    pool = multiprocessing.Pool(processes=3)
    # 向进程池中添加任务
    for i in range(5):
        pool.apply_async(worker, args=(i,))
    # 关闭进程池,不再接受新的任务
    pool.close()
    # 等待所有任务完成
    pool.join()
    print('All workers done.')

在这个例子中,我们首先创建了一个进程池,池中有3个进程。然后向进程池中添加了5个任务,每个任务都是调用worker函数。最后,我们关闭了进程池,并等待所有任务完成。

需要注意的是,进程池中的任务必须是可序列化的,因为进程池会将任务发送给子进程执行。如果任务中包含不可序列化的对象,会导致进程池无法正常工作。

第四章:协程

协程是一种轻量级的线程,也称为微线程或者用户级线程。协程的特点是在一个线程中,可以有多个协程,协程之间可以相互切换,从而实现并发执行。

在Python中,协程是通过生成器实现的。通过yield关键字,可以将一个函数变成一个生成器,从而实现协程的功能。在协程中,可以使用yield关键字来暂停函数的执行,并返回一个值给调用者。当协程再次被调用时,可以从上一次暂停的位置继续执行。

Python中的协程有两种实现方式:使用生成器实现的协程和使用async/await关键字实现的协程。

使用生成器实现的协程

def coroutine():
    while True:
        value = yield
        print('Received value:', value)

c = coroutine()
next(c)  # 启动协程
c.send(10)  # 发送值给协程

使用async/await关键字实现的协程

import asyncio

async def coroutine():
    while True:
        value = await asyncio.sleep(1)
        print('Received value:', value)

loop = asyncio.get_event_loop()
loop.run_until_complete(coroutine())

在使用async/await关键字实现的协程中,需要使用asyncio模块提供的事件循环来运行协程。在协程中,可以使用await关键字来暂停函数的执行,并等待一个异步操作完成。当异步操作完成后,协程会从await语句处继续执行。

协程的优点是可以避免线程切换的开销,从而提高程序的性能。同时,协程也可以避免线程之间的竞争条件和死锁问题。但是,协程也有一些缺点,例如不能利用多核CPU的优势,以及不能进行阻塞式IO操作。

1.协程的意义

协程是一种轻量级的线程,可以在单个线程中实现并发。与线程相比,协程的切换开销更小,可以更高效地利用CPU资源。协程的意义在于:

  • 提高程序的并发性能:协程可以在单个线程中实现并发,避免了线程切换的开销,提高了程序的并发性能。
  • 简化编程模型:协程可以使用同步的编程模型,避免了复杂的线程同步问题,使得编程更加简单。
  • 支持高并发:协程可以支持大量的并发任务,可以用于高并发的网络编程、爬虫等场景。
  • 提高代码可读性:协程可以使用同步的编程模型,代码可读性更高,易于维护。

总之,协程是一种高效、简单、可读性强的并发编程模型,可以提高程序的并发性能,支持高并发,简化编程模型。

2.asyncio事件循环

在Python中,协程是一种轻量级的并发编程方式,它可以在单线程中实现并发执行。协程的意义在于可以提高程序的并发性能,减少线程切换的开销,同时也可以简化编程模型,使得代码更加易于理解和维护。

在Python 3.4及以上版本中,标准库中提供了asyncio模块,它是Python中实现协程的主要方式之一。asyncio模块提供了一个事件循环(Event Loop),它可以在单线程中实现多个协程的并发执行。事件循环会不断地从协程队列中取出协程并执行,当协程遇到IO操作时,会自动挂起并切换到其他协程执行,等待IO操作完成后再恢复执行。

asyncio事件循环的使用方式如下:

1.创建一个事件循环对象

import asyncio

loop = asyncio.get_event_loop()

2.将协程对象加入事件循环中

async def coroutine():
    # 协程代码

loop.run_until_complete(coroutine())

3.启动事件循环

loop.run_forever()

在事件循环中,可以使用async/await关键字定义协程函数,使用asyncio模块提供的各种方法实现协程之间的通信和协作。例如,可以使用asyncio.sleep()方法实现协程的延时操作,使用asyncio.wait()方法等待多个协程的完成等。

3.await关键字

在Python中,await是一个关键字,用于等待一个协程完成。当一个协程调用另一个协程时,它可以使用await关键字来等待另一个协程完成并返回结果。在等待期间,当前协程会被挂起,直到被等待的协程完成。

例如,假设有两个协程A和B,A需要等待B完成后才能继续执行。在协程A中,可以使用await关键字来等待协程B完成:

async def coroutine_b():
    # 协程B的代码

async def coroutine_a():
    # 协程A的代码
    result = await coroutine_b()
    # 继续执行协程A的代码

在这个例子中,当协程A调用await coroutine_b()时,它会等待协程B完成并返回结果。在等待期间,协程A会被挂起,直到协程B完成。一旦协程B完成并返回结果,协程A会继续执行。

使用await关键字可以使协程之间的调用更加简洁和直观,同时也可以避免使用回调函数等复杂的异步编程模式。

4.concurrent和future对象

在Python中,asyncio模块提供了一种基于协程的异步编程方式。在协程中,我们可以使用async/await关键字来定义异步函数,使用asyncio模块提供的事件循环来调度协程的执行。

除了协程之外,asyncio还提供了一些其他的并发编程工具,包括concurrentfuture对象。

  • concurrent对象

concurrent对象是asyncio中的一个重要概念,它表示一个协程的执行状态。在asyncio中,我们可以使用asyncio.create_task()函数来创建一个concurrent对象,该函数接受一个协程对象作为参数,并返回一个concurrent对象。

例如,下面的代码创建了一个协程对象,并使用create_task()函数将其转换为concurrent对象:

import asyncio

async def my_coroutine():
    print('Coroutine started')
    await asyncio.sleep(1)
    print('Coroutine ended')

async def main():
    task = asyncio.create_task(my_coroutine())
    await task

asyncio.run(main())

在上面的代码中,我们使用create_task()函数将my_coroutine()函数转换为concurrent对象,并将其赋值给task变量。然后,我们使用await关键字等待task对象的完成。

  • future对象

future对象是asyncio中的另一个重要概念,它表示一个异步操作的结果。在asyncio中,我们可以使用asyncio.Future()函数来创建一个future对象,该函数返回一个未完成的future对象。

例如,下面的代码创建了一个未完成的future对象:

import asyncio

async def my_coroutine():
    print('Coroutine started')
    await asyncio.sleep(1)
    print('Coroutine ended')
    return 'Result'

async def main():
    future = asyncio.Future()
    await asyncio.sleep(1)
    future.set_result(await my_coroutine())
    print(future.result())

asyncio.run(main())

在上面的代码中,我们使用asyncio.Future()函数创建了一个未完成的future对象,并将其赋值给future变量。然后,我们使用await关键字等待1秒钟,然后调用my_coroutine()函数,并将其结果设置为future对象的结果。最后,我们打印future对象的结果。

5.asyncio异步迭代器和上下文管理

除了concurrent和future对象之外,asyncio还提供了一些其他的并发编程工具,包括异步迭代器和上下文管理。

异步迭代器是一种特殊的迭代器,它可以在异步环境中使用。在asyncio中,我们可以使用async for循环来遍历异步迭代器。

例如,下面的代码使用async for循环遍历一个异步迭代器:

import asyncio

async def my_coroutine():
    for i in range(5):
        await asyncio.sleep(1)
        yield i

async def main():
    async for i in my_coroutine():
        print(i)

asyncio.run(main())

在上面的代码中,我们定义了一个异步生成器函数my_coroutine(),它使用yield语句返回一个值,并在每次返回值之间暂停1秒钟。然后,我们使用async for循环遍历my_coroutine()函数返回的异步迭代器,并打印每个返回值。

上下文管理是一种在异步环境中管理资源的方式。在asyncio中,我们可以使用async with语句来管理异步上下文。

例如,下面的代码使用async with语句管理一个异步上下文:

import asyncio

class MyContext:
    async def __aenter__(self):
        print('Entering context')
        await asyncio.sleep(1)
        return self

    async def __aexit__(self, exc_type, exc, tb):
        print('Exiting context')
        await asyncio.sleep(1)

async def main():
    async with MyContext() as context:
        print('Inside context')

asyncio.run(main())

在上面的代码中,我们定义了一个MyContext类,它实现了__aenter__()__aexit__()方法。aenter()方法在进入上下文时被调用,aexit()方法在退出上下文时被调用。在main()函数中,我们使用async with语句管理MyContext对象,并在上下文中打印一条消息。当我们进入和退出上下文时,aenter()__aexit__()方法会被调用,并暂停1秒钟。

6.异步操作MySQL

在Python中,我们可以使用异步IO库asyncio来实现异步操作MySQL数据库。下面是一个简单的示例:

import asyncio
import aiomysql

async def test_mysql():
    # 连接MySQL数据库
    conn = await aiomysql.connect(host='localhost', port=3306,
                                  user='root', password='password',
                                  db='test', charset='utf8mb4')
    # 创建游标
    cur = await conn.cursor()
    # 执行SQL语句
    await cur.execute("SELECT * FROM users")
    # 获取查询结果
    result = await cur.fetchall()
    # 输出查询结果
    print(result)
    # 关闭游标和连接
    await cur.close()
    conn.close()

# 运行异步函数
loop = asyncio.get_event_loop()
loop.run_until_complete(test_mysql())

在上面的示例中,我们使用了aiomysql库来连接MySQL数据库,并使用async/await语法来执行异步操作。首先,我们使用aiomysql.connect()方法来连接MySQL数据库,然后使用await conn.cursor()方法创建游标,使用await cur.execute()方法执行SQL语句,使用await cur.fetchall()方法获取查询结果,最后使用await cur.close()方法关闭游标,使用conn.close()方法关闭连接。

需要注意的是,在使用aiomysql库时,我们需要在连接MySQL数据库时指定charset='utf8mb4',以支持中文字符集。

7.异步爬虫

异步爬虫是指使用协程来实现爬虫程序,通过异步非阻塞的方式来提高爬取效率。在Python中,可以使用asyncio库来实现异步爬虫。

下面是一个简单的异步爬虫示例:

import asyncio
import aiohttp
from bs4 import BeautifulSoup

async def fetch(session, url):
    async with session.get(url) as response:
        return await response.text()

async def get_links(session, url):
    html = await fetch(session, url)
    soup = BeautifulSoup(html, 'html.parser')
    links = []
    for link in soup.find_all('a'):
        href = link.get('href')
        if href and href.startswith('http'):
            links.append(href)
    return links

async def main():
    async with aiohttp.ClientSession() as session:
        links = await get_links(session, 'https://www.baidu.com')
        for link in links:
            print(link)

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

在这个示例中,我们使用了aiohttp库来发送异步HTTP请求,使用BeautifulSoup库来解析HTML页面,使用asyncio库来实现协程。

首先定义了一个fetch函数,用于发送HTTP请求并返回响应内容。然后定义了一个get_links函数,用于获取页面中的所有链接。最后,在main函数中使用aiohttp库创建一个异步HTTP客户端会话,调用get_links函数获取链接,并打印出来。

需要注意的是,在使用aiohttp库时,需要使用async with语句来创建一个异步HTTP客户端会话,以确保会话在使用完毕后能够正确关闭。

下章讲:python数据库编程:Redis数据库

更多关注

  • 1.面试PDF+100道大厂面试
  • 2.入门基础教程
  • 3.11模块零基础到精通笔记
  • 4.百个项目实战+爬虫教程+代码
  • 5.量化交易,机器学习,深度学习
  • 6.Python游戏源码
  • 7.交流学习

文档+笔记+源码获取方式

#python打卡##每天学python#?#python##python自学#?#怎样学好python#?

文档+笔记+了解接单+源码获取方式

创业项目群,学习操作 18个小项目,添加 微信:luao319  备注:小项目

本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 fqkj163@163.com 举报,一经查实,本站将立刻删除。
如若转载,请注明出处:https://www.fqkj168.cn/12945.html