进程时一个任务的执行过程,在这个过程中实际做事的是进程,线程是开在进程里面的,需要先有进程,在有线程,一个进程中至少有一个线程,当然,也可以有多个线程
进程是资源分配的基本单位,线程是CPU执行的最小单位,每一个进程中至少有一个线程
进程和线程都是由操作系统来调度的,协程它是由程序员来调度的
进程 >>>> 线程 >>>> 协程
资源消耗是最多的 >>>> 线程的资源 >>>> 协程的
在python中我们一般开多进程,而不开多线程,在其他语言中,都是选择开多线程
需要通过模块from threading improt Thread
然后实例化再使用
target = task
(必要)name = '****'
args= (, )
kwargs= {}
t.name
和t.getName()
t.daemon = True
和t.setDaemon(true)
t.start()
t.is_alive()
t.setName('****')
from threading import Thread
def task():
pass
if __name__ == '__main__':
t = Thread(target=task)
t.start()
from threading import Thread
def task():
pass
if __name__ == '__main__':
lst = []
for i in range(3):
t = Thread(target=task)
t.start()
lst.append(t)
for i in lst:
i.join()
CPython IPython PyPy Jython
计算密集型选多进程好些,在其他语言中,都是选择多线程,而不选择多进程。
多个线程去操作同一个数据,会出现并发安全问题,怎么解决呢?
加锁
互斥锁:多个线程同时操作同一个数据,会发生数据不安全问题,这时个时候我们给一个线程加上互斥锁之后,必须等待一个线程执行完毕,其他线程才能执行
进程之间的数据是隔离的,所以,我们使用了队列来实现进程之间的通信
进程Queue用于父进程与子进程(或同一父进程中多个子进程)间数据传递
python自己的多个进程之间交换数据或者其他语言(Java)进程queue就无能为力
queue.Queue
的缺点是它的实现涉及多个锁和条件变量,因此可能会影响性能和内存效率
只要加锁,比会影响性能和效率!但是好处就是保证数据安全
import queue
q = queue.Queue()
q.put(1)
q.get()
import queue
# Lifo:last input first output
q = queue.LifoQueue()
q.put(1)
q.get()
import queue
q = queue.PriorityQueue()
# put进入一个元组,元组的第一个元素是优先级(通常是数字,也可以是非数字之间的比较)
# 数字越小优先度越高
q.put(('a', 'b'))
q.get()
池子:容器,盛放多个元素值
进程池:存放多个线程的
线程池:存放多个线程的
进程池:提前定义一个池子,里面放很多个进程,只需要往池子里丢任务即可,由这个池子里任意一个进程来执行任务
线程池:提前定义一个池子,里面放很多个线程,只需要往池子里丢任务即可,由这个池子里任意一个线程来执行任务
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
def task(a, b): # 进程函数
return a + b
def callback(res): # 回调函数
res = res.result() + 1
print(res)
return res # 可要可不要
if __name__ == '__main__':
# 定义池子最多进程,数字是几就是几
pool = ThreadPoolExecutor(5)
# 往池子里放任务,如过不需要返回值
# pool.submit(task, 2, 3)
# 需要返回值则需要一个回调函数,需要提前写好
pool.submit(task, 2, 3).add_done_callback(callback)
pool.shutdown() # 进程池运行玩后关闭池子 join + close
用于解决高并发问题,在进程池的范围内,爬取大量的网页会在进程池在面等待进入
import requests
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
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)
进程:进程解决高并发问题
线程:解决高并发问题
协程:它是单线程下的并发,它是程序员级别的,我们来控制如何切换,何时切换
进程和线程是操作系统来控制的,我们控制不了
协程是一种用户态(程序员)的轻量级线程,即协程是由用户程序自己控制调度的
进程的开销 >>>> 线程的开销 >>>> 协程的开销
协程的使用需要借助于第三方模块gevent模块
必须线安装: pip install geven
在协程中遇到io会切换单如果遇见time.sleep()
不会切,需换成gevent.sleep()
import gevent
def a(aa):
pass
if __name__ == '__main__':
# 开启协程,后面直接传参数
g = gevent.spawn(a, 123)
g.join()
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()