Clojure 并發(fā)編程

2018-12-29 17:08 更新

在Clojure編程中,大多數(shù)數(shù)據(jù)類(lèi)型是不可變的,因此當(dāng)涉及并發(fā)編程時(shí),使用這些數(shù)據(jù)類(lèi)型的代碼在多個(gè)處理器上運(yùn)行時(shí)是相當(dāng)安全的。 但是許多次,需要共享數(shù)據(jù),并且當(dāng)涉及多個(gè)處理器的共享數(shù)據(jù)時(shí),有必要確保在使用多個(gè)處理器時(shí)保持?jǐn)?shù)據(jù)的狀態(tài)的完整性。 這被稱(chēng)為并發(fā)編程,Clojure提供對(duì)這種編程的支持。

通過(guò)dosync,ref,set,alter等暴露的軟件事務(wù)存儲(chǔ)器系統(tǒng)(STM)支持以同步和協(xié)調(diào)的方式共享線程之間的變化狀態(tài)。 代理系統(tǒng)支持以異步和獨(dú)立的方式共享線程之間的變化狀態(tài)。 原子系統(tǒng)支持以同步和獨(dú)立的方式共享線程之間的變化狀態(tài)。 而通過(guò)def,綁定等暴露的動(dòng)態(tài)var系統(tǒng)支持隔離線程內(nèi)的變化狀態(tài)。

其他編程語(yǔ)言也遵循并行編程模型。

  • 它們直接引用可以更改的數(shù)據(jù)。

  • 如果需要共享訪問(wèn),則對(duì)象被鎖定,值被更改,并且進(jìn)程繼續(xù)下一次訪問(wèn)該值。

在Clojure中沒(méi)有鎖,但是對(duì)不可變持久數(shù)據(jù)結(jié)構(gòu)的間接引用。

Clojure中有三種類(lèi)型的引用。

  • Vars -更改在線程中隔離。

  • Refs -更改在線程之間進(jìn)行同步和協(xié)調(diào)。

  • Agents -涉及線程之間的異步獨(dú)立變化。

在Clojure中有關(guān)并發(fā)編程的以下操作是可能的。

事務(wù)

Clojure中的并發(fā)是基于事務(wù)。 引用只能在事務(wù)中更改。 在事務(wù)中應(yīng)用以下規(guī)則。

  • 所有的變化都是atomic和孤立的。
  • 對(duì)引用的每個(gè)更改都發(fā)生在事務(wù)中。
  • 沒(méi)有事務(wù)看到另一個(gè)事務(wù)所造成的影響。
  • 所有事務(wù)都放在dosync塊中。

我們已經(jīng)看到了dosync塊做了什么,讓我們?cè)倏纯础?

dosync

在包含表達(dá)式和任何嵌套調(diào)用的事務(wù)中運(yùn)行表達(dá)式(在隱式do中)。 如果此線程上沒(méi)有運(yùn)行,則啟動(dòng)事務(wù)。 任何未捕獲的異常將中止事務(wù)并流出dosync。

以下是 dosync 的基本語(yǔ)法。

語(yǔ)法

(dosync expression)

參數(shù) - 'expression'是將在dosync塊中出現(xiàn)的一組表達(dá)式。

返回值 -無(wú)。

讓我們看一個(gè)例子,其中我們?cè)噲D改變一個(gè)引用變量的值。

(ns clojure.examples.example
   (:gen-class))
(defn Example []
   (def names (ref []))
   (alter names conj "Mark"))
(Example)

輸出

上述程序運(yùn)行時(shí)出現(xiàn)以下錯(cuò)誤。

Caused by: java.lang.IllegalStateException: No transaction running
   at clojure.lang.LockingTransaction.getEx(LockingTransaction.java:208)
   at clojure.lang.Ref.alter(Ref.java:173)
   at clojure.core$alter.doInvoke(core.clj:1866)
   at clojure.lang.RestFn.invoke(RestFn.java:443)
   at clojure.examples.example$Example.invoke(main.clj:5)
   at clojure.examples.example$eval8.invoke(main.clj:7)
   at clojure.lang.Compiler.eval(Compiler.java:5424)
   ... 12 more

從錯(cuò)誤中,您可以清楚地看到,您不能在不首先啟動(dòng)事務(wù)的情況下更改引用類(lèi)型的值。

為了使上面的代碼工作,我們必須把a(bǔ)lter命令放置在dosync塊中,如下面的程序所做。

(ns clojure.examples.example
   (:gen-class))
(defn Example []
   (def names (ref []))
   
   (defn change [newname]
      (dosync
         (alter names conj newname)))
   (change "John")
   (change "Mark")
   (println @names))
(Example)

上述程序產(chǎn)生以下輸出。

輸出

[John Mark]

讓我們看另一個(gè)dosync的例子。

(ns clojure.examples.example
   (:gen-class))
(defn Example []
   (def var1 (ref 10))
   (def var2 (ref 20))
   (println @var1 @var2)
   
   (defn change-value [var1 var2 newvalue]
      (dosync
         (alter var1 - newvalue)
         (alter var2 + newvalue)))
   (change-value var1 var2 20)
   (println @var1 @var2))
(Example)

在上面的例子中,我們有兩個(gè)值在dosync塊中被改變。 如果事務(wù)成功,則兩個(gè)值都將改變,否則整個(gè)事務(wù)將失敗。

上述程序產(chǎn)生以下輸出。

輸出

10 20
-10 40

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)