我有一个使用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。
有人知道这个实现中有什么问题吗?我所知道的是,在我的应用程序生命周期中,原子被共享到并发过程,为什么会出现这个问题?
答案 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
类型的性质提供了两个函数peek
和pop
来使项目出列。 dosync
宏确保传递给它的所有表达式将同步应用。这样它可以确保两个线程不会查看同一个项目并弹出两次。