ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
[TOC] ## 1. 进程 ### 1.1 fork > * 适用于Unix/Linux系统,不适合Windows > * 每次调用fork函数都会创建两个进程(父子进程) > * 使用计算密集型的任务 ~~~ import os rpid = os.fork() if rpid<0: print("fork调⽤失败。 ") elif rpid == 0: print("我是⼦进程(%s) , 我的⽗进程是(%s) "%(os.getpid(),os.getppid())) x+=1 else: print("我是⽗进程(%s) , 我的⼦进程是(%s) "%(os.getpid(),rpid)) print("⽗⼦进程都可以执⾏这⾥的代码") ~~~ > 1. 调用fork进程后,回返回两个结果,这两个结果时对应的两个进程 子进程:rpid==0 父进程:rpid >0 > 2. 调用fork函数后的代码,都会在父子进程中执行,可以理解为fork后边的代码被分发到了父子进程中去执行,但是执行互不影响(进程资源独立) ### 1.2 multiprocessing > * fork适用于Unix/Linux系统,但是不适用于Windows平台,但是Python是跨平台的语言,难道Windows就不能写多进程任务了吗?答案肯定是no。 > * multiprocessing模块是就是解决上面问题的,可以实现真正的跨平台 ~~~ from multiprocessing import Process import os # ⼦进程要执⾏的代码 def run_proc(name): print('⼦进程运⾏中, name= %s ,pid=%d...' % (name, os.getpid())) if __name__=='__main__': print('⽗进程 %d.' % os.getpid()) p = Process(target=run_proc, args=('test',)) print('⼦进程将要执⾏') p.start() p.join() print('⼦进程已结束') ~~~ ## 2. 线程 ### 2.1 创建线程 > * 线程由进程创建,并且多个线程共享进程的资源,不像进程间通信那样费劲 > * 适合I/O(网络、磁盘)量比较大的任务,比如爬虫(多网络、多磁盘I/O) 1. 直接使用Thread类构建线程 ~~~ from threading import Thread import threading import os def thread(): print("-----------thread--------%s",threading.current_thread().name) def main(): for i in range(5): p = Thread(target=thread) p.start() if __name__ == '__main__': main() ~~~ 2. 继承Thread类,并重写run方法 ~~~ from threading import Thread import os class SubThread(Thread): def run(self): print("子进程----name:"+self.name) print(os.getpid()) for i in range(10): subThread = SubThread() subThread.start() ~~~ ### 2.2 进程锁 ~~~ import threading import time lock1 = threading.Lock() lock2 = threading.Lock() class SubThread(threading.Thread): def run(self): while True: if lock1.acquire(): # 每次获取锁都会停在这,所以只会打印一次 time.sleep(1) print("--------------1------------") lock1 = threading.Lock() class SubThread2(threading.Thread): def run(self): while True: if lock2.acquire(): # 每次获取锁都会停在这,所以只会打印一次 time.sleep(1) print("--------------2------------") lock2.release() # 释放锁,下次lock2.acquire()就可以获取到锁 t1 = SubThread() t2 = SubThread2() t1.start() t2.start() ~~~ * 打印结果 ~~~ --------------1------------ --------------2------------ --------------2------------ --------------2------------ --------------2------------ --------------2------------ --------------2------------ ~~~ 说明虽然在一个线程中,只要调用一次 lock2.acquire()方法,就会寻找锁,所有第一个线程只能打印一次,第二个线程无限的打印 * 利用这一性质实现多个线程的协作,无限的顺序打印 ~~~ from threading import Thread,Lock from time import sleep lock1 = Lock() lock2 = Lock() lock2.acquire() #创建另外⼀把锁, 并且“锁上” lock3 = Lock() lock3.acquire() class Task1(Thread): def run(self): while True: if lock1.acquire(): print("------Task 1 -----") sleep(0.5) lock2.release() class Task2(Thread): def run(self): while True: if lock2.acquire(): print("------Task 2 -----") sleep(0.5) lock3.release() class Task3(Thread): def run(self): while True: if lock3.acquire(): print("------Task 3 -----") sleep(0.5) lock1.release() t1 = Task1() t2 = Task2() t3 = Task3() t1.start() t2.start() t3.start() ~~~ 打印结果如下 ~~~ ------Task 1 ----- ------Task 2 ----- ------Task 3 ----- ------Task 1 ----- ------Task 2 ----- ------Task 3 ----- ... ~~~ ## 3. 协程 ## 4. 单路复用 ### 4.1 select select最早于1983年出现在4.2BSD中,它通过一个select()系统调用来监视多个文件描述符的数组(在linux中一切事物皆文件,块设备,socket连接等。),当select()返回后,该数组中就绪的文件描述符便会被内核修改标志位(变成ready),使得进程可以获得这些文件描述符从而进行后续的读写操作(select会不断监视网络接口的某个目录下有多少文件描述符变成ready状态【在网络接口中,过来一个连接就会建立一个'文件'】,变成ready状态后,select就可以操作这个文件描述符了)。 在不使用多进程和多线程的前提下,实现高并发服务器有三种方式select、poll、epoll ~~~ import select import socket import sys server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(('', 7788)) server.listen(5) inputs = [server, sys.stdin] running = True while True: readable, writeable, exceptional = select.select(inputs, [], []) # 阻塞等待 for sock in readable: if sock == server: conn, addr = server.accept() inputs.append(conn) elif sock == sys.stdin: cmd = sys.stdin.readline() running = False break else: data = sock.recv(1024) if data: sock.send(data) else: inputs.remove(sock) sock.close() if not running: break ~~~ select()方法接收并监控3个通信列表, 第1个是所有的输入的data,就是指外部发过来的数据, 第2个是监控和接收所有要发出去的data(outgoing data),当一个socket进来后,他就会进入可写队列 第3个监控错误信息,接下来我们需要创建2个列表来包含输入和输出信息来传给select().