环应用行为中的Clojure atom PersistentQueue

时间:2017-07-16 02:34:29

标签: clojure atomicity compojure ring

我有一个使用compojure的铃声应用程序。 我使用PersistentQueue创建了一个原子来存储流程执行的ID,并使用相同的ID阻止重复执行和其他请求到我的API。

但在我的测试中,Atom工作得很好,但只在我的API的同一端点。 如果我呼叫其他端点,行为就不同了。

我的原子:

private void fetchMovies() {
    String url = "http://www.url.com/test.json";
    JsonObjectRequest req = new JsonObjectRequest(url, null, 
            new Response.Listener<JSONObject>() {
                @Override
                public void onResponse(JSONObject response) {
                    try {
                        JSONArray level = response.getJSONArray("level");
                        JSONObject item = level.getJSONObject(0);
                        JSONArray server = item.getJSONArray("server");
                        for (int i = 0; i < server.length(); i++) {
                            try {
                                JSONObject movieObj = server.getJSONObject(i);
                                int rank = movieObj.getInt("rank");
                                String title = movieObj.getString("title");
                                Movie m = new Movie(rank, title);
                                movieList.add(0, m);
                            } catch (JSONException e) {
                            }
                        }
                        adapter.notifyDataSetChanged();
                    } catch (JSONException e) {
                    }
                }
            }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            Toast.makeText(getApplicationContext(), error.getMessage(), Toast.LENGTH_LONG).show();
        }
    });
    MyApplication.getInstance().addToRequestQueue(req);
}

为了说明,当我调用端点http://localhost:3000/run时,调用函数add-to-queue并将我的Atom内容交换到具有一个id的队列。

原子状态:

(def queue (atom clojure.lang.PersistentQueue/EMPTY))

(defn add-to-queue [number]
  (swap! queue conj number))

(defn remove-from-queue [number] 
  (swap! queue (fn [q] (remove #{number} q))))

(defn queue-has-id? [number]
  (let [pr-queue (seq @queue)]
    (if-not (nil? pr-queue)
      (> (.indexOf pr-queue number) -1)
      false)))

在我的流程执行期间,如果我呼叫端点&#39; RUN&#39;再次,我调用了函数queue-has-id?要阻止id是否存在,在这种情况下,id&#39; 1&#39;存在然后执行被阻止。

但如果我调用其他ENDPOINT&#39; retrieve&#39;,我的原子队列值为[1],但indexOf id返回false。

有人知道这个实现中有什么问题吗?我所知道的是,在我的应用程序生命周期中,原子被共享到并发过程,为什么会出现这个问题?

1 个答案:

答案 0 :(得分:0)

首先,您不要以惯用的方式使用队列。队列是一种抽象数据类型,它为具有下一个操作的项目提供有序容器:

  • 排队,conj clojure.lang.PersistentQueue
  • 出列,peek获取头部项目,pop返回没有头部项目的队列
  • 可选,空白检查,empty?

我认为您需要一个提供存储唯一编号(ID)的集合,并在需要时将其删除。我可以建议使用set数据结构。在这种情况下,您的代码应该看起来像

(def numbers (atom #{}))

(defn add-number! [n]
  (swap! numbers conj n))

(defn contains-number? [n]
  (contains? @numbers n))

(defn remove-number! [n]
  (swap! numbers disj n))

但如果您出于某些原因仍想使用PersistentQueue,您的代码应该看起来像

(def queue (ref clojure.lang.PersistentQueue/EMPTY))

(defn enqueue!
  "It doesn't check uniqueness."
  [item]
  (dosync (alter queue conj item)))

(defn has-item?
  "Be careful, it is O(n) operation."
  [item]
  (some #(= item %) @queue))

(defn dequeue!
  "Pop element off the queue and returns it"
  []
  (dosync
   (let [v (peek @queue)]
     (alter queue pop)
     v)))

(defn remove!
  "Because you want to remove all repetition of an item you should
   convert current a queue to a sequence, remove items and convert
   it back to a queue. It creates intermediate collection and it is
   WRONG way to use queue. But it is still achievable."
  [item]
  (dosync
   (alter queue #(apply conj
                        clojure.lang.PersistentQueue/EMPTY
                        (remove #{item} %)))))

如您所见,我使用ref代替atom,因为PersistentQueue类型的性质提供了两个函数peekpop来使项目出列。 dosync宏确保传递给它的所有表达式将同步应用。这样它可以确保两个线程不会查看同一个项目并弹出两次。