4

あまり知識がなかったときに書いた Python コードの一部を整理しています。主に、Python のスレッド化についての不完全な理解に起因する複雑さの一部を解消しています。アイテムのリストをスレッドセーフにする必要があり、通常のロック方法ではなく、不変リストを介して行いたいと考えています。不変オブジェクトは、スレッド化に関して非常に特別であることを私は知っています。不完全な状態変更を取り巻くすべてのスレッド セーフの問題が単純に解消されるからです。

それで、私は尋ねます: 次のコードはスレッドセーフですか?

class ImmutableList(object):
    def __init__(self):
        self._list = ()

    def __iter__(self):
        return self._list.__iter__()

    def append(self, x):
        self._list = self._list + tuple([x])

毎回新しいリストが作成されるため、そうであると思います。別のスレッドがリストを反復している間にリストが更新された場合、反復の残りの部分では古いリストが引き続き使用されます。これは私には問題ありませんが、万人向けではないかもしれません。

また、これは良い考えですか?リストのサイズが小さく、リストがあまり変更されないいくつかの状況にのみこれを適用したいと思います (イベント リスナーが思い浮かびます)。

4

2 に答える 2

15

まず第一に、Python プログラミング言語の CPython 参照実装では、リストへの追加は既にスレッドセーフです。つまり、言語仕様ではリスト クラスがスレッド セーフである必要はありませんが、いずれにしてもそうです。したがって、Jython や IronPython、またはそのような他の Python 実装を使用していない限り、問題ありません。

__setitem__次に、 andなどの他のリスト操作もオーバーロードする必要があり__setslice__ます。実装でこれが処理されると想定しています。

最後に、あなたの質問に対する答えはノーです。あなたのコードはスレッド セーフではありません。次の状況を考慮してください。

  • あなたのリストには (5, 6) が含まれています
  • スレッド 1 は 7 を追加しようとし、スレッド 2 は 8 を追加しようとします
  • スレッド 1 は別のタプル (5、6、7) を構築し、それを _list に割り当てる前に、コンテキスト スイッチがあります。
  • スレッド 2 が割り当てを実行するため、リストは (5, 6, 8) になります。
  • スレッド 1 は CPU の制御を取り戻し、_list に割り当て、前の追加を上書きします。リストは現在 (5, 6, 7) で、8 は失われています。

この話の教訓は、ロックを使用し、巧妙さを避けるべきだということです。

于 2009-04-13T02:35:47.597 に答える
4

真の不変リストの実装では、ここにあるように、基になるリスト構造を変更することはできません。@[Eli Courtwright] が指摘したように、実装はスレッドセーフではありません。それは、実際には不変ではないからです。不変の実装を作成するには、リストを変更するメソッドは、代わりに、目的の変更を反映した新しいリストを返します。

コード例に関しては、次のようなことを行う必要があります。

class ImmutableList(object):
  def __init__(self):
    self._list = ()

  def __iter__(self):
    return self._list.__iter__()

  def append(self, x):
    return self._list + tuple([x])
于 2009-04-13T02:57:27.373 に答える