基本的なことを誤解していて、存在しない問題を作成しているため、あなたの質問は基本的に無意味です。コメントで回答してみましたが、それでは限界があるので…</p>
App Engine は動的ファイルに従来の Python ファイル名を提供できないため、ファイルのようなオブジェクトを使用せずにすべてを実行したいと考えています。
ファイルのようなオブジェクトには、ファイル名やファイルは必要ありません。これが、ファイルのようなオブジェクトの背後にある全体的な考え方です。
App Engine には、ファイルのようなオブジェクトを開く方法はありません (ファイルがアプリの一部としてアップロードされた場合を除く)。
いいえ、まだファイル オブジェクトとファイルのようなオブジェクトを混同しています。ファイル オブジェクトは、ディスク上の実際のファイルを表します。GAE はそれらを制限します。ファイルに似たオブジェクトとは、同じ API を持つ任意のオブジェクトです。つまり、実際には (必ずしも) ファイルではなく、ファイルのように動作するオブジェクトです。GAE は、ユーザーがファイルのようなオブジェクトを作成できないようにすることは何もしません。
--
ファイルのようなオブジェクトのパラダイム例である a はStringIO.StringIO
、ファイル オブジェクトのように動作しますが、実際にファイルを読み書きする代わりに、メモリ内の文字列バッファーを読み書きします。
したがって、次のように書き込み用にファイルのようなオブジェクトを開くことができます。
my_file_like_obj = StringIO()
または、メモリ内にバッファがあり、ファイルのように読み取ることができるようにしたい場合:
my_file_like_obj = StringIO(buffer)
ただし、Python/GAE が既に提供しているファイルのようなオブジェクトを、バッファーに読み取って別のファイルのようなオブジェクトにラップすることなく、そのまま使用できる場合が多くあります。多くのネットワーク API はファイルのようなオブジェクトを返しますが、すべてではありません。
たとえば、 を呼び出すurllib2.urlopen
と、結果はファイルのようなオブジェクトになります。を呼び出す場合urlfetch.fetch
はそうではないため、必要な場合は使用StringIO(response.content)
する必要があります。
そのため、GAE が Linux ボックスから圧縮データを取得した場合、圧縮モジュールがファイルのようなオブジェクトを通過するように要求した場合、圧縮を解除する方法がわかりません。
ファイルのようなオブジェクトを要求する場合は、ファイルのようなオブジェクトを指定してください。実際のファイルを作成することは、それを行う 1 つの方法ですが、唯一の方法ではありません。urllib2.urlopen
応答がある場合は、それを渡します。メモリにバッファがある場合は、StringIO
. 等々。
たとえば、gzip モジュールはファイル名を持つことを主張します: http://docs.python.org/2/library/gzip.html
いいえ、そうではありません。リンク先のドキュメントを読んでください。
class gzip.GzipFile([ファイル名[, モード[, 圧縮レベル[, ファイルオブジェクト[, mtime]]]]])
fileobj
パラメータだけでなくパラメータもあることに注意してfilename
ください。そして、ドキュメントの最初の行には次のように書かれています:
… fileobjとfilenameの少なくとも 1 つに重要な値を指定する必要があります …</p>
したがって、 isfilename
でない限りfileobj
None
、 を持つことを主張しません。それを回避するには、ただ… を渡さないNone
でくださいfileobj
。
実際のファイル オブジェクトである必要がありますかfileobj
、それとも別のファイルのようなオブジェクトにすることができますか? さて、次の段落は次のように述べています。
… 新しいクラス インスタンスは、通常のファイル、オブジェクト、またはファイルをシミュレートするその他のオブジェクトであるfileobjに基づいています。StringIO
では、どうぞ。
残念ながら、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
また、 a自体がファイルのようなオブジェクトであることにも注意してください。これは重要です。これは、物事を連鎖できることを意味するためです。 から を作成し、GzipFile
からStringIO
を作成するTarFile
などGzipFile
です。
Linux での「スレッドセーフ」 = アプリケーションは Web サーバーの背後にあるため、圧縮および/または圧縮解除を同時に実行するために別々のスレッドが呼び出される可能性があります。
それは問題ではありません。もう一度、リンク先のドキュメントを読んでください。
複数のスレッドから単一の LZMAFile インスタンスを使用する必要がある場合は、ロックで保護する必要があります。
LZMAFile
複数の独立したインスタンスの圧縮および/または解凍は問題ではありません。スレッド間で同じインスタンスを共有したい場合のみです。そして、そうする正当な理由はほとんどありません。
Linux アプリは、圧縮データの数千 (数百万のうち) のセミランダム チャンクをディスクから読み取ることから始めます。次に、それぞれを圧縮解除し、圧縮解除された各チャンクを変更し、変更されたチャンクを圧縮して、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
のは、最後だけです。
現在、アプリは zlib を使用しており、cherrypy 内で軽負荷の下で問題なく動作しますが、リクエストが並行して発生し始めるとすぐに zlib エラーが発生します。
あなたのコードについてもっと知らなければ、それをデバッグするのは非常に難しいですが、私は良い推測をしています.一時ファイルに書き込むことによって圧縮を行っていますtempfile
.これは、スレッドが互いの一時ファイルを上書きしてしまうことを意味します。
bz2 に関して、個別のロックに関するコメントは何を意味しますか
確かに、それは少し混乱しています。それが言うすべては次のとおりです。
- スレッドセーフは、個別のロック機構を使用しています。
これは明らかにスレッドセーフであることを意味しますが、なぜそれらが使用するロック機構を気にするのでしょうか? とにかく「個別ロック機構」とは何ですか?
ソースを見ればわかります)。
つまり、各BZ2Compressor
(およびBZ2Decompressor
) オブジェクトには独自の個別のロックがあるため、そのうちの 1 つが他のオブジェクトに影響を与えることなくロックできます。
Python C 拡張機能のスレッド化を扱ったことがない場合は、これが何を意味するのか理解できないかもしれません。通常、Python では、すべてのスレッドが作業を行うためにGILを保持する必要があります。つまり、一度に実行できるスレッドは 1 つだけです。しかし、C 拡張モジュールは、Python 以外のオブジェクトで CPU を大量に使用する作業 (たとえば、大きなバッファーの圧縮など) を実行しているときに、GIL を解放できます。N 個のスレッドが GIL を解放する場合、最大 N+1 個のスレッドを並行して実行できます。つまり、複数のプロセスを実行することなく、8 コアの CPU から大きな利点を得ることができます。ただし、ロックで保護しない限り、GIL が解放されている間は Python オブジェクトに触れることはできません。
高速化のために GIL を解放する多くのモジュールは、単一のモジュール ロックを作成します (コードがどのオブジェクトに触れる可能性があるかを把握するのがそれほど簡単ではないため)。つまり、そのモジュールの処理を実行する 1 つのスレッドを、他の処理を実行するスレッドと並行して実行できますが、そのモジュールの処理を実行するスレッドは 1 つしか実行できません。
ただし、各スレッドが 1 つのオブジェクトにのみアクセスする必要がある場合は、オブジェクトごとに異なるロックを使用できます。つまり、すべてのスレッドが異なるオブジェクトで動作している限り、必要な数のスレッドを並行して実行できます。
同時に 2 つのスレッドで同じオブジェクトを使用しようとしても、何も壊れません。片方のスレッドがロックを取得するのをもう一方のスレッドが完了するまで待機することになります (これは、GIL で待機することよりも良くも悪くもありません)。