《GO併發程式設計實戰》 只會執行一次

2021-09-23 16:13:02 字數 3020 閱讀 1444

現在,讓我們再次聚焦到sync**包。除了我們介紹過的互斥鎖、讀寫鎖和條件變數,該**包還為我們提供了幾個非常有用的api。其中乙個比較有特色的就是結構體型別sync.once和它的do方法。

與代表鎖的結構體型別sync.mutex和sync.rwmutex一樣,sync.once也是開箱即用的。換句話說,我們僅需對它進行簡單的宣告即可使用,就像這樣:

1var once sync.once

2

3once.do(func() )

如上所示,我們宣告了乙個名為once的sync.once型別的變數之後,立刻就可以呼叫它的指標方法do了。

該型別的方法do可以接受乙個無引數、無結果的函式值作為其引數。該方法一旦被呼叫,就會呼叫被作為引數傳入的那個函式。從這一點看,該方法的功能實在是稀鬆平常。不過,重點並不在這裡。

我們對乙個sync.once型別值的指標方法do的有效呼叫次數永遠會是1。也就是說,無論我們呼叫這個方法多少次,也無論我們在多次呼叫時傳遞給它的引數值是否相同,都僅有第一次呼叫是有效的。無論怎樣,只有我們第一次呼叫該方法時傳遞給它的那個函式會被執行。請看下面的示例:

01func oncedo()

10}

11fori :=0; i <3; i++

15forj :=0; j <3; j++

22}

23fmt.printf("num: %d.\n", num)

24}

在oncedo函式中,我們利用for語句連續三次非同步的呼叫once變數的do方法。這三次呼叫傳給do方法的引數值都是相同的,都是變數fi所代表的匿名函式值。這個函式值的功能是先改變num變數的值再向非緩衝的sign通道傳送乙個true。變數num的值可以表示出once的do方法被有效呼叫的次數,而通道sign則被用來傳遞代表了fi函式被執行完畢的訊號。請注意,為了能夠精確的表達出fi函式是在哪一次(或哪幾次)呼叫once.do方法的時候被執行的,我們在這裡使用了閉包。在每次迭代之初,我們賦給fi變數的函式值都是對變數f所代表的函式值進行閉包的乙個結果值。我們使用變數ii作為f函式中的自由變數,並在閉包的過程中把for**塊中的變數i的值加1後再與該自由變數繫結在一起。這樣就生成了為當次迭代專門定製的函式fi。每次迭代中生成的fi函式在被執行的時候都會修改變數num的值。這些新的值不會出現重複,並且非常有助於我們倒推出所有的曾賦給自由變數的ii的值。這樣,我們就可以知道哪個(或哪些)fi函式被真正的執行了。

函式oncedo中的第二條for語句的作用是等待之前的那三個非同步呼叫的完成。讀者可能已經發現,這兩條for語句的預設迭代次數是一致的。在第二條for語句中,我們使用了select語句,並且為針對sign通道的接收操作設定了超時時間(100毫秒)。這是為了當永遠無法從sign通道中接收元素值的時候不至於造成永久的阻塞。select語句中的每個case在被執行時都會列印出相應的內容。這有助於我們觀察程式的實際運**況。最後,我們還會列印出num變數的值。據此,我們可以判斷在前面幾次傳遞給do方法的fi是否都被執行了。

在執行oncedo函式之後,我們會看到如下列印內容:

1received a signal.

2

3timeout!

4

5timeout!

6

7num:2.

上面的列印內容表明,在成功從sign通道接收了乙個元素值之後,出現了兩次接收操作超時的情況。我們不用考慮在對sign通道的接收操作開始之時匿名函式fi還沒有被執行完畢的情況。因為100毫秒的時間已經足夠執行它很多很多次的了。因此,這兩次接收操作超時應該是當時沒有正在為此等待的對sign通道的傳送操作導致的(注意,sign是乙個非緩衝通道)。綜上所述,我們可以初步判斷,傳遞給once.do方法的匿名函式fi只被執行了一次。並且,這僅有一次的執行的物件是在我們第一次呼叫該方法時傳遞給它的那個fi函式。

依據最後一行列印內容,我們可以證實上述判斷。num變數的值為2意味著它只被修改了一次,並且是在自由變數ii為1的時候被修改的。這就可以證實,只有在for迴圈的第一次迭代時傳遞給once.do方法的那個fi函式被執行了。這也符合sync.once型別及其指標方法do的語義。

請注意,這個僅被執行一次的限制只是針對單個sync.once型別值來說的。換句話說,每個sync.once型別值的指標方法do都可以被有效的呼叫一次。

這個sync.once型別的典型應用場景就是執行僅需執行一次的任務。例如,資料庫連線池的初始化任務。又例如,一些心跳檢測之類的實時監測任務。等等。

在一探sync.once型別及其指標方法do的內部實現之後,我們會有所發現:它們所提供的功能正是由前面講到的互斥鎖和原子操作來實現的。這個實現並不複雜。其使用的技巧包括衛述語句、雙重檢查鎖定,以及對共享標記的原子讀寫操作。在熟知了本章講述的這些同步工具

之後,我們是否也能輕易設計出這樣簡單、有效的解決方案呢?

總之,sync.once型別及其方法實現了「只會執行一次」的語義。我們在需要完成只需或只能執行一次的任務的時候應該首先想到它。

sqlmap一次實戰

這是墨者學院乙個sql注入漏洞靶場 目的是尋找key 從這個網頁提示資訊我們也可以窺視出一些東西 它使用的資料庫型別是mysql key的值就儲存於變數裡面 很可能key值就是我們苦苦尋找的flag,那肯定是儲存在資料庫裡面的 先對其進行手工注入 你們懂得就是 在url後面分別新增and 1 1 和...

一次sql server實戰

前言 朋友在做授權專案的時候,遇到乙個sql server資料庫的注入點,沒辦法解決,讓我幫忙看看,因為是授權專案,所以就可以幫助測試下。內容如下 單引號,很明顯的錯誤,因為是時間格式 2020 6 2,所以這裡肯定是字元型的。接著測試 還是語法錯誤,那麼可能因為有括號的原因,接著測試 這裡出現了關...

java併發程式設計實戰 筆記 任務執行

excutor框架 excutor框架將任務的提交和執行分離開,任務提交會遍布到程式的各個地方,但執行策略的設定可以在乙個地方,如用執行緒池執行還是序列執行,執行緒池固定長度還是變長,所有顯示地使用new thread task 的地方都應該考慮下excutor框架 攜帶結果的任務callable和...