分享

Python [de]压缩模块在Linux上是否是线程安全的?在Google App Engine上?

 印度阿三17 2019-06-29

我应该使用什么压缩 – 解压缩Python模块来构建一个系统,其中Google App Engine(Python 2.7)与Linux机器上的应用程序交换压缩数据?

还有两个额外的限制:

> Linux机器和GAE都将进行压缩/解压缩,并且需要安全地操作线程;
>我想在不使用类似文件的对象的情况下完成所有操作,因为App Engine无法为动态文件提供传统的Python文件名.

我问,因为从文档中不清楚某些[de]压缩模块是否是线程安全的.

任何人都可以帮忙填写压缩模块表吗?

> bz2:IS SAFE,每个:http://docs./2/library/bz2.html,但有一个关于个别锁定的评论让我想知道我是否需要明确管理锁.
> zlib 😕 – 发布GIL,每个:http://docs./2/c-api/init.html
      但基础C代码被认为是安全的:http://www./zlib/zlib_faq.html#faq21
> gzip 😕
> lzma:不安全,按:http://docs./3.4/library/lzma

谢谢!

编辑(回答abarnert的问题):

> RAM与类文件对象= App Engine不提供打开类文件对象的方法(除非文件是作为应用程序的一部分上传的).因此,如果GAE从Linux盒子中获取压缩数据,如果压缩模块坚持要通过类似文件的对象,我不知道如何解压缩它.例如,gzip模块坚持使用文件名:http://docs./2/library/gzip.html
> Linux上的“线程安全”=应用程序将位于Web服务器后面,因此可能会同时调用单独的线程进行压缩和/或解压缩. Linux应用程序首先从磁盘中读取几千个(数百万个)半随机块压缩数据,然后解压缩每个,然后更改每个未压缩的块,然后压缩更改的块,然后发送到GAE.现在,该应用程序使用zlib并在cherrypy中轻负载下运行完美,但是一旦请求开始并行发生,就会引发zlib错误.一旦这个压缩事情被整理出来,我就会切换到nginx.

解决方法:

你的问题基本上没有意义,因为你误解了一些基本的东西并产生了不存在的问题.我试着在评论中回答,但是你可以用这种方式做限,所以…

I want to do everything without using file-like objects, because App Engine cannot provide conventional Python filenames for dynamic files.

对于类文件对象,您不需要文件名或文件.这就是文件类对象背后的整个想法.

App Engine does not provide a way to open a file-like object (unless the file was uploaded as part of the app).

不,您仍然混淆文件对象和类文件对象. A file object表示磁盘上的实际文件. GAE限制了那些.类似文件的对象是具有相同API的任何对象,即一个像文件一样的对象,而不必(实际上)是一个. GAE没有做任何事情来阻止您创建类似文件的对象.

类文件对象(StringIO.StringIO)的范例示例就像一个文件对象,但它不是实际读写文件,而是读写内存中的字符串缓冲区.

因此,您可以像这样编写一个类似文件的对象:

my_file_like_obj = StringIO()

或者,如果你在内存中有一个缓冲区,并且你希望能够像文件一样从中读取:

my_file_like_obj = StringIO(buffer)

但是,在许多情况下,Python / GAE已经为您提供了一个类似文件的对象,您可以按原样使用它,而无需将其读入缓冲区并将其包装在另一个类似文件的对象中.许多网络API为您提供类似文件的对象,但不是全部.

例如,如果调用urllib2.urlopen,则结果是类文件对象;如果你调用urlfetch.fetch,它不是,所以如果你需要的话,你必须使用StringIO(response.content).

So if GAE gets compressed data from the Linux box, I don’t know of a way to uncompress it if the compression module insists that I go through a file-like object.

如果它坚持使用类似文件的对象,请为其提供类似文件的对象.创建实际文件是一种方法,但不是唯一的方法.如果你有一个urllib2.urlopen响应,那就试试吧.如果内存中有缓冲区,只需将其包装在StringIO中即可.等等.

The gzip module, for example, insists on having a filename: 07002

不,不.阅读您链接到的文档:

class gzip.GzipFile([filename[, mode[, compress level[, fileobj[, mtime]]]]])

请注意,有一个fileobj参数以及一个文件名参数?文档的第一行说:

… At least one of fileobj and filename must be given a non-trivial value …

因此,除非fileobj为None,否则它不会坚持使用文件名.为了解决这个问题,请…不要为fileobj传递None.

fileobj必须是真正的文件对象,还是可以是另一个类似文件的对象?那么,下一段说:

… The new class instance is based on fileobj, which can be a regular file, a StringIO object, or any other object which simulates a file.

所以,你去吧.

不幸的是,Python 2.x对于什么算作类似文件的对象并不是100%一致,并且文档并不总是很清楚. (这在3.x中被清理了很多,但是如果你使用GAE,这对你没有任何好处.)

如果某些API不喜欢您的类文件对象,因为它不能模拟足够的API,您将通过获取AttributeError找到答案.例如,您可能会收到一条错误消息,指出您从urllib2.urlopen返回的对象没有seek属性.

解决方法很简单:将其读入内存并创建StringIO.换句话说,只需将fileobj = my_file_obj更改为fileobj = StringIO(my_file_obj.read()).

另请注意,GzipFile本身就是一个类文件对象.这很重要,因为这意味着你可以将事物链接在一起 – 你可以用StringIO创建一个GzipFile,然后用GzipFile创建一个TarFile,依此类推.

“Thread-safe” on linux = Application will be behind a webserver, and so separate threads will be likely be called upon to compress and/or decompress at the same time.

那不是问题.再次,阅读您链接到的文档:

if you need to use a single LZMAFile instance from multiple threads, it is necessary to protect it with a lock.

压缩和/或解压缩多个独立的LZMAFile实例不是问题.只有当您想要跨线程共享相同的实例时.并且几乎没有充分的理由这样做.

The Linux app starts by reading a few thousand (out of many million) semi-random chunks of compressed data from disk, then uncompressing each, then altering each uncompressed chunk, then compressing the altered chunks, then sending to GAE.

您所谈论的所有压缩机都是流式压缩机.在不压缩文件的情况下,无法从文件中间解压缩任意块.

这对我来说意味着你实际拥有的是一堆独立压缩的块(无论是在单独的文件中,还是连接成一个文件都不清楚,但并不重要).

这意味着您无需在任何地方共享解压缩程序或压缩程序.例如:

with lzma.LZMAFile(chunk_path) as f:
    decompressed_chunk = f.read()
new_chunk = alter(decompressed_chunk)
sio = StringIO.StringIO()
with lzma.LZMAFile(fileobj=sio) as f:
    f.write(new_chunk)
compressed_chunk = sio.getvalue()
send_to_gae(compressed_chunk)

线程之间没有什么可以分享的.即使200个线程同时执行此操作,即使其中100个线程正在尝试处理相同的块文件,仍然不会出现问题.唯一需要排序的是最后的send_to_gae.

Right now, the app uses zlib and runs flawlessly under light load within cherrypy, but raises zlib errors as soon as requests start happening in parallel.

在不了解您的代码的情况下,调试它非常困难,但我有一个很好的猜测:您通过写入临时文件来进行压缩,而不是在tempfile中使用安全API,而是重新发明了轮子,有独特的错误,这意味着你最终会用线程覆盖彼此的临时文件.

what does the comment about individual locking mean with respect to bz2

不可否认,它有点令人困惑.它只是说:

>螺纹安全使用单独的锁定机制.

这显然意味着它的线程安全,但为什么你关心它们使用什么锁定机制?什么是“个人锁定机制”呢?

你只能看看the source).

它们的意思是每个BZ2Compressor(和BZ2Decompressor)对象都有自己独立的锁,因此其中一个可以锁定而不会影响其他对象.

如果您还没有处理Python C扩展中的线程,您可能无法理解这是什么.通常,在Python中,每个线程都需要保持GIL才能完成任何工作,这意味着一次只能运行一个线程.但是C扩展模块可以释放GIL,同时使用非Python对象(例如,压缩大缓冲区)进行CPU繁重的工作.如果N个线程释放GIL,则最多可以并行运行N 1个线程,这意味着您可以在不运行多个进程的情况下从您的8核CPU中获得很大的优势.但是,除非用锁保护它们,否则在释放GIL时不能触摸任何Python对象.

许多发布GIL以加速的模块会创建一个模块锁(有时因为找出代码可能触及的对象并不容易).这意味着你可以运行一个线程来执行该模块的东西,并与执行其他操作的线程并行执行,但不会有多个线程执行该模块的操作.

但是,如果每个线程只需要触摸单个对象,则可以为每个对象使用不同的锁,这意味着您可以并行运行任意数量的线程,只要它们都在处理不同的对象.

如果你试图同时在两个线程中使用同一个对象,它不会破坏任何东西;你将最终得到一个线程等待获取锁定,直到另一个完成(这比等待GIL更好或更差).

来源:https://www./content-3-280451.html

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多