0

次の構造を持つ非常に単純なハッシュテーブルを考えてみましょう。

(defn make-hashtable
  [cap]
  (let 
    [tablesize (int (Math/ceil (* cap 1.30)))]
      {:tablesize tablesize
     :capacity cap
       :size (ref 0)
       :vector (vec (map (fn [_] (ref '())) (range tablesize)))
       :type :hashtable}))

put、、 などの基本的な操作がgetありremoveます。を使用して並行してテストするためのいくつかのテストを作成しましたpcalls。最初に 1 つの " " で実行するテストを開発しthread(つまり、1 つの関数を使用して を呼び出すだけpcalls)、成功したため、問題はそこにはなく、removeスレッドセーフでない関数にあると想定する必要があります。

削除は次のように実装されます。

(defn mht-remove
  [table key]
  (let [pos (table-pos table key)]
    (dosync 
      (let [bucket @((:vector table) pos)]
        (ensure (:size table))
        (ensure ((:vector table) pos))
        (defn equalskey?
          [x]
          (= (:key x) key))
        (when (some equalskey? bucket)
            (ref-set ((:vector table) pos) (remove equalskey? bucket))
            (alter (:size table) dec))))))

奇妙なことに、たった 1 つの要素のバケットが削除されないことさえあります。ベクトルの要素のみが参照であり、ベクトル全体ではないため、これは奇妙です。 質問: このコードがスレッドセーフでないのはなぜですか?


注:多くの人がこのアプリケーションの設計についてコメントしようとすることは承知していますが、これは私が興味を持っている種類の回答ではありません。純粋な機能コードを記述するよりも、clojure の STM を理解したいのです。

4

1 に答える 1

0

これはグローバルな副作用であり、トランザクションを台無しにするため、ブロック内defまたはブロックdefn内では実行できないようです。dosyncドキュメントによると副作用が許可されていないことを除いて、私は完全には理解していませんが、これがすべてを台無しにしています。

Freenodeのみんなに感謝#clojure

于 2013-06-09T16:57:58.447 に答える