需求场景:
由于业务需求,需要将保存在OSS上的图片数据,以用户的角度去拉取,为了减少代码量所以放弃了java采取了python,由于数据总量在200w张照片,所以需要多线程去拉取节省时间。
一、进程与线程的联系和区别
线程是该进程的一条执行路径,是程序执行时的最小单位,它是进程的一个执行流,负责CPU调度和分派,一个进程可以由很多个线程组成,线程间共享进程的所有资源,每个线程有自己的堆栈和局部变量。线程由CPU独立调度执行,在多CPU环境下就允许多个线程同时运行。同样多线程也可以实现并发操作,每个请求分配一个线程来处理
二、python中的进程与多进程
多进程如果将进程池数量开启过多,容易导致电脑死机,所以采取多线程的方式
import time
import socket
import urllib.request as request
import threading
from logging import getLogger, INFO
from concurrent_log_handler import ConcurrentRotatingFileHandler
import os
import multiprocessing
# 请求oss下载超时时间
socket.setdefaulttimeout(120)
# 记录日志
log = getLogger()
logfile = os.path.abspath("wo.log")
rotateHandler = ConcurrentRotatingFileHandler(logfile, "a", 512 * 1024, 5)
log.addHandler(rotateHandler)
log.setLevel(INFO)
# 将远程数据下载到本地,第二个参数就是要保存到本地的文件名
def test(xx, y, q): # Use thread.start_new_thread() to create 2 new threads
pass # 下载数据
if __name__ == '__main__':
# 开启一个新进程 multiprocessing.process(方法,args=(方法参数,))
# multiprocessing.process(test,args=(xx,y,q,)
multiprocessing.freeze_support()
# 开启进程池
p = multiprocessing.Pool(10)
for i in range(1, 11):
if i == 1:
star = 0
else:
star = (i - 1) * 35000
p.apply_async(test, args=(result, star, i * 35000,)) # 异步方式去开启进程
p.close()
p.join()
print("所有进程执行完毕")
三、python中的线程与多线程
# -*- coding: UTF-8 -*-
import threading
from time import ctime
import csv # 系统内置模块
import socket
import urllib.request as request
from logging import getLogger, INFO
from concurrent_log_handler import ConcurrentRotatingFileHandler
import os
socket.setdefaulttimeout(120)
log = getLogger()
# Use an absolute path to prevent file rotation trouble.
logfile = os.path.abspath("test.log")
# Rotate log after reaching 512K, keep 5 old copies.
rotateHandler = ConcurrentRotatingFileHandler(logfile, "a", 512 * 1024, 5)
log.addHandler(rotateHandler)
log.setLevel(INFO)
class myThread(threading.Thread):
def __init__(self, threadID, name, s, e):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.s = s
self.e = e
def run(self):
print("Starting " + self.name + ctime())
# 获得锁,成功获得锁定后返回True
# 可选的timeout参数不填时将一直阻塞直到获得锁定
# 否则超时后将返回False
# 线程需要执行的方法
threadLock.acquire()
# 线程需要执行的方法
printImg(self.s, self.e)
# 释放锁
threadLock.release()
# 按照分配的区间,读取列表内容,需要其他功能在这个方法里设置
def printImg(s, e):
pass
totalThread = 21 # 需要创建的线程数,可以控制线程的数量
#
threadLock = threading.Lock() # 锁
threads = [] # 创建线程列表
# 创建新线程和添加线程到列表
for i in range(1, 21):
thread = 'thread%s' % i
if i == 1:
star = 0
else:
star = (i - 1) * 18000
thread = myThread(i, "Thread-%s" % i, star, i * 18000)
threads.append(thread) # 添加线程到列表
thread.start() # 开启线程
# 等待所有线程完成
for t in threads:
# 依次检验线程池中的线程是否结束,没有结束就阻塞直到线程结束,如果结束则跳转执行下一个线程的join函数。
t.join()
print("Exiting Main Thread")
四、python中的线程池或进程池(推荐使用这个)
前面两个写法都有点太不优雅,无法更好的管理多线/进进程。所以引入了线/进程池的概念,可以理解为开一家租车店(线/进程池),一共有20辆车(20个线/进程),有人来租车的时候,找一个没有被租赁的车给他,使用完放回车库,等下一个有需求的人进来在分配,这样可以更好的管理多线/进程。
import csv # 系统内置模块
import socket
import urllib.request as request
from logging import getLogger, INFO
from concurrent_log_handler import ConcurrentRotatingFileHandler
import os
from concurrent.futures import ThreadPoolExecutor
socket.setdefaulttimeout(120)
log = getLogger()
# Use an absolute path to prevent file rotation trouble.
logfile = os.path.abspath("zz.log")
# Rotate log after reaching 512K, keep 5 old copies.
rotateHandler = ConcurrentRotatingFileHandler(logfile, "a", 512 * 1024, 5)
log.addHandler(rotateHandler)
log.setLevel(INFO)
def mkdir(path):
# 去除首位空格
path = path.strip()
# 去除尾部 \ 符号
path = path.rstrip("\\")
# 判断路径是否存在
# 存在 True
# 不存在 False
isExists = os.path.exists(path)
# 判断结果
if not isExists:
# 如果不存在则创建目录
# 创建目录操作函数
os.makedirs(path)
# print (path + ' 创建成功')
return True
else:
return False
# 将远程数据下载到本地,第二个参数就是要保存到本地的文件名
#
# urllib.request.urlretrieve('链接',
# 'D:/1.jpg')
# csv文件读写 csv每一行用,分割 换行则是切行
with open('q2w.csv') as fileRead:
f_csv = csv.reader(fileRead)
result = list(f_csv)
def test(y, q):
print(str(y) + '-' + str(q - 1))
for count in range(y, q):
row = result[count]
ss = 'E:/test/' + row[2]
mkdir(ss)
reul = ''
try:
if not os.path.exists(ss + '/01_' + row[3] + '.png'):
request.urlretrieve(row[0],
ss + '/01_' + row[3] + '.png')
reul += ','
except Exception as e:
print("m")
reul += str(row[0]) + ","
print(e)
try:
if not os.path.exists(ss + '/02_' + row[3] + '.png'):
if row[1] is not None and row[1] != '':
request.urlretrieve(row[1],
ss + '/02_' + row[3] + '.png')
reul += ','
except Exception as e:
print("f" + str(row[1]))
reul += str(row[1]) + ','
print(e)
if __name__ == '__main__':
# 开启线程池 ThreadPoolExecutor(max_workers(线程的最多数量),thread_name_prefix(线程池内线程的名称前缀))
threadPool = ThreadPoolExecutor(max_workers=40, thread_name_prefix="test_")
# 开启进程池 ProcessPoolExecutor(max_workers(进程的最多数量),thread_name_prefix(进程池内线程的名称前缀))
for i in range(1, 37):
if i == 1:
star = 0
else:
star = (i - 1) * 10000
# submit('当前线程需要执行的具体逻辑',参数1,参数2)
future = threadPool.submit(test, star, i * 10000)
print("所有进程执行完毕")