在 python 中使用多线程下载文件
Downloading file using multithreading in python
我正在尝试使用 python cgi 中的多线程将多个文件 (ard 25k) 放入一个 zip 文件中。我已经编写了下面的脚本,但不知何故,我得到的响应的内容长度为 0,并且响应中没有数据。这是我第一次在 python 中使用多线程。代码中有什么我遗漏的吗?是否在发布数据之前就打印了输出?
任何帮助将不胜感激。
这是我的代码:
b = StringIO()
z = zipfile.ZipFile(b, 'w', zipfile.ZIP_DEFLATED)
def read_file(link):
fname = link.split('/')
fname = fname[-1]
z.write(link, fname)
if __name__ == '__main__':
form = cgi.FieldStorage()
fileLinks = form.getvalue("fileLink")
p = Pool(10)
p.map(read_file, fileLinks)
p.close()
p.join()
z.close()
zipFilename = "DataFiles-" + str(time.time()) + ".zip"
length = b.tell()
sys.stdout.write(
HEADERS % ('application/zip', zipFilename, zipFilename, length)
)
b.seek(0)
sys.stdout.write(b.read())
b.close()
相同代码的后续版本:
for fileLink in fileLinks:
fname = fileLink.split('/')
filename = fname[-1]
z.write(fileLink, filename)
z.close()
问题应该是ZipFile.write()
(通常是ZipFile
)不是线程安全的。
您必须以某种方式序列化对 zip 文件的线程访问。这是一种方法(在 Python 3 中):
ziplock = threading.Lock()
def read_file(link):
fname = link.split('/')
fname = fname[-1]
with ziplock:
z.write(link, fname)
这样做应该没有任何好处,因为锁有效地做的是序列化 zip 文件的创建。
这个版本可以实现一些并行化,它在将文件内容添加到 zip 文件之前读取文件内容:
def read_file(link):
fname = link.split('/')
fname = fname[-1]
# the file is read in parallel
contents = open(link).read()
with ziplock:
# writes to the zip file a re serialized
z.writestr(fname, contents)
但是,如果文件驻留在同一个文件系统上,则读取操作很可能就好像它们已被操作系统序列化一样。
因为它是文件,可能的 target 并行化将是过程的 CPU-bound 部分,即压缩,而 [=34 似乎不可能=]zip 格式(因为 zip
文件的行为类似于目录,所以每个 write()
都必须保持状态准备好在 close()
上生成完整的存档)。
如果您可以使用不同的压缩格式,那么使用 gizp 进行压缩并使用 tar 可以在没有锁定的情况下进行并行化(tarfile
) 作为存档格式,因为每个文件都可以并行读取和压缩,只有 tar 串联会串行完成(.tar.gz
或 .tgz
存档格式)。
我正在尝试使用 python cgi 中的多线程将多个文件 (ard 25k) 放入一个 zip 文件中。我已经编写了下面的脚本,但不知何故,我得到的响应的内容长度为 0,并且响应中没有数据。这是我第一次在 python 中使用多线程。代码中有什么我遗漏的吗?是否在发布数据之前就打印了输出?
任何帮助将不胜感激。
这是我的代码:
b = StringIO()
z = zipfile.ZipFile(b, 'w', zipfile.ZIP_DEFLATED)
def read_file(link):
fname = link.split('/')
fname = fname[-1]
z.write(link, fname)
if __name__ == '__main__':
form = cgi.FieldStorage()
fileLinks = form.getvalue("fileLink")
p = Pool(10)
p.map(read_file, fileLinks)
p.close()
p.join()
z.close()
zipFilename = "DataFiles-" + str(time.time()) + ".zip"
length = b.tell()
sys.stdout.write(
HEADERS % ('application/zip', zipFilename, zipFilename, length)
)
b.seek(0)
sys.stdout.write(b.read())
b.close()
相同代码的后续版本:
for fileLink in fileLinks:
fname = fileLink.split('/')
filename = fname[-1]
z.write(fileLink, filename)
z.close()
问题应该是ZipFile.write()
(通常是ZipFile
)不是线程安全的。
您必须以某种方式序列化对 zip 文件的线程访问。这是一种方法(在 Python 3 中):
ziplock = threading.Lock()
def read_file(link):
fname = link.split('/')
fname = fname[-1]
with ziplock:
z.write(link, fname)
这样做应该没有任何好处,因为锁有效地做的是序列化 zip 文件的创建。
这个版本可以实现一些并行化,它在将文件内容添加到 zip 文件之前读取文件内容:
def read_file(link):
fname = link.split('/')
fname = fname[-1]
# the file is read in parallel
contents = open(link).read()
with ziplock:
# writes to the zip file a re serialized
z.writestr(fname, contents)
但是,如果文件驻留在同一个文件系统上,则读取操作很可能就好像它们已被操作系统序列化一样。
因为它是文件,可能的 target 并行化将是过程的 CPU-bound 部分,即压缩,而 [=34 似乎不可能=]zip 格式(因为 zip
文件的行为类似于目录,所以每个 write()
都必须保持状态准备好在 close()
上生成完整的存档)。
如果您可以使用不同的压缩格式,那么使用 gizp 进行压缩并使用 tar 可以在没有锁定的情况下进行并行化(tarfile
) 作为存档格式,因为每个文件都可以并行读取和压缩,只有 tar 串联会串行完成(.tar.gz
或 .tgz
存档格式)。