次の構造を持つ非常に単純なハッシュテーブルを考えてみましょう。
(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 を理解したいのです。