3

私が思いついたすべての解決策は、スレッド保存ではありません。

def uuid(cls,db):
    u = hexlify(os.urandom(8)).decode('ascii')
    db.execute('SELECT sid FROM sessions WHERE sid=?',(u,))
    if db.fetch(): u=cls.uuid(db)
    else: db.execute('INSERT INTO sessions (sid) VALUES (?)',(u,))
    return u
4

9 に答える 9

5
import os, threading, Queue

def idmaker(aqueue):
  while True:
    u = hexlify(os.urandom(8)).decode('ascii')
    aqueue.put(u)

idqueue = Queue.Queue(2)

t = threading.Thread(target=idmaker, args=(idqueue,))
t.daemon = True
t.start()

def idgetter():
  return idqueue.get()

Queue は、Python でスレッドを同期するための最良の方法であることがよくあります。マルチスレッド システムを設計するとき、最初に考えるべきことは、「これを Queue でどのように行うのが最善か」ということです。根底にある考え方は、スレッドを専用にして共有リソースまたはサブシステムを完全に「所有」し、他のすべての「ワーカー」スレッドに、その専用スレッドによって使用されるキューへの取得および/または書き込みによってのみリソースにアクセスさせることです (キューは本質的にスレッドセーフです)。 .

ここではidqueue、長さが 2 だけの を作成します (事前に大量の ID を作成して、ID の生成が暴走するのは望ましくありません。これにより、メモリが浪費され、エントロピー プールが使い果たされます。これ2が最適かどうかはわかりませんが、 spot は間違いなく非常に小さな整数になります;-)、したがって id 生成スレッドは 3 番目のものを追加しようとするとブロックされ、キューにスペースが空くまで待機します。 idgetter(トップレベルの割り当てによって単純に定義することもできますidgetter = idqueue.get) は、通常、ID が既にそこにあり、待機しているのを見つけます (そして、次の ID のためにスペースを空けます!) -- そうでない場合は、本質的にブロックして待機し、 ID ジェネレーターが新しい ID をキューに入れました。

于 2009-11-06T15:50:08.773 に答える
3

あなたのアルゴリズムは問題なく (DB API モジュールが安全である限り、スレッドセーフです)、おそらく最善の方法です。重複することはありませんが(sidにPRIMARYまたはUNIQUEキーがあると仮定)、IntegrityErrorINSERTで例外が発生する可能性はほとんどありません。しかし、あなたのコードは見栄えがよくありません。再帰の代わりに試行回数を制限したループを使用することをお勧めします (コードに何らかのエラーが発生した場合、無限になる可能性があります)。

for i in range(MAX_ATTEMPTS):
    sid = os.urandom(8).decode('hex')
    db.execute('SELECT COUNT(*) FROM sessions WHERE sid=?', (sid,))
    if not db.fetchone()[0]:
        # You can catch IntegrityError here and continue, but there are reasons
        # to avoid this.
        db.execute('INSERT INTO sessions (sid) VALUES (?)', (sid,))
        break
else:
    raise RuntimeError('Failed to generate unique session ID')

失敗する可能性をさらに小さくするために使用されるランダムな読み取り文字数を上げることができます。base64.urlsafe_b64encode()SID を短くしたい場合は、あなたの友人ですが、データベースがこの列に対して大文字と小文字を区別する比較を使用していることを確認する必要があります (バイナリ照合を設定しない限り、MySQL の VARCHAR は適切ではありませんが、VARBINARY は問題ありません)。

于 2009-11-06T13:53:49.523 に答える
2

スレッドセーフが必要な場合は、共有ロックを使用する関数を乱数ジェネレーターに入れてみませんか。

import threading
lock = threading.Lock()
def get_random_number(lock)
    with lock:
        print "This can only be done by one thread at a time"

呼び出しているすべてのスレッドget_random_numberが同じロックインスタンスを使用している場合、一度に1つだけが乱数を作成できます。

もちろん、このソリューションを使用して、アプリケーションにボトルネックを作成したばかりです。一意の識別子のブロックを作成し、それらを並行して使用するなど、要件に応じて他のソリューションがあります。

于 2009-11-06T12:56:38.993 に答える
1

私が思うデータベースを呼び出す必要はありません:

>>> import uuid

# make a UUID based on the host ID and current time
>>> uuid.uuid1()
UUID('a8098c1a-f86e-11da-bd1a-00112444be1e')

このページから.

于 2009-11-06T12:47:07.580 に答える
1

スレッド固有の ID から始めて、それを (どういうわけか) スレッド ローカル カウンターと連結し、暗号化ハッシュ アルゴリズムを介してフィードします。

于 2009-11-06T12:51:36.340 に答える
0

mkdtemp は、スレッドセーフでシンプルかつ安全でなければなりません:

def uuid():
    import tempfile,os
    _tdir = tempfile.mkdtemp(prefix='uuid_')
    _uuid = os.path.basename(_tdir)
    os.rmdir(_tdir)
    return _uuid
于 2009-11-07T21:45:57.980 に答える
0

データベースに対して uid を検証し、競合状態を回避する必要がある場合は、トランザクションを使用します。

BEGIN TRANSACTION
SELECT COUNT(*) FROM sessions WHERE sid=%s
INSERT INTO sessions (sid,...) VALUES (%s,...)
COMMIT
于 2009-11-06T12:50:51.627 に答える
0

各スレッドに固有のデータはありませんか? まったく同じデータを持つ 2 つのスレッドを想像するのは難しいです。私は可能性を軽視しませんが。

過去に私がこのような性質のことをしたとき、通常、糸には何か独特なものがあります。ユーザー名またはクライアント名、またはその性質のもの。私にとっての解決策は、たとえば、UserName とミリ秒単位の現在の時刻を連結し、その文字列をハッシュしてハッシュの 16 進ダイジェストを取得することでした。これにより、常に同じ長さの素敵な文字列が得られます。

2 つのスレッド内の 2 つの異なる John Smith (またはその他のもの) が同じミリ秒で ID を生成する可能性はほとんどありません。その可能性が神経質になる場合は、前述のロック ルートが必要になる場合があります。

既に述べたように、GUID を取得するためのルーチンが既に存在します。私は個人的にハッシュ関数をいじるのが好きなので、大規模なマルチスレッドシステムで成功した方法で自分自身をロールバックしました。

重複データを含むスレッドが実際に存在するかどうかを最終的に判断するのは、ユーザー次第です。必ず適切なハッシュ アルゴリズムを選択してください。私は md5 を正常に使用しましたが、md5 とのハッシュ衝突を生成する可能性があることを読みましたが、それを行ったことはありません。最近はsha1を使っています。

于 2009-11-06T17:11:53.850 に答える