我在Go中編寫了一個搜索引擎,在該搜索引擎中,每個單詞對應的結果都有一個單詞倒排索引。有一組單詞字典,因此這些單詞已經轉換爲StemID
,這是一個從0開始的整數。這使我可以使用一個指針片段(即一個sparse array
)將每個StemID
映射到包含該查詢的結果。例如。 var StemID_to_Index []*resultStruct
。如果aardvark
是0
那麼指向aardvark
的resultStruct的指針位於StemID_to_Index[0]
,如果此字的結果當前未加載,則該指針將爲nil
。轉到:使用稀疏陣列讀寫的線程安全併發問題
服務器上沒有足夠的內存將所有內容存儲在內存中,因此每個StemID
的結構都將保存爲單獨的文件,這些文件可以加載到StemID_to_Index
切片中。如果StemID_to_Index
當前爲nil
這StemID
然後結果不緩存,需要加載,否則它已經加載(緩存),因此可以直接使用。每次加載新結果時都會檢查內存使用情況,如果超出閾值,則會丟棄2/3的加載結果(對於這些StemID,StemID_to_Index
設置爲nil
,並強制執行垃圾回收)。
我的問題是併發性。什麼是最快和最有效的方式,我可以同時搜索多個線程,而不會遇到不同線程同時嘗試讀取和寫入同一個地方的問題?我試圖避免在一切中使用互斥鎖,因爲這會減慢每一次訪問嘗試的速度。
您是否認爲我會在工作線程中加載磁盤的結果,然後使用通道將指向此結構的指針傳遞到「更新程序」線程,然後將StemID_to_Index
切片中的nil
值更新爲加載結果的指針?這意味着兩個線程永遠不會同時嘗試寫入,但是如果另一個線程嘗試從StemID_to_Index
的確切索引中讀取,而「updater」線程正在更新指針時會發生什麼情況?如果一個線程被賦予了一個nil
指針,這對於當前正在加載的結果是沒有關係的,因爲它只會被加載兩次,雖然這是浪費資源,但仍然會提供相同的結果,並且因爲這不太可能經常發生,這是可以原諒的。
此外,發送指針的工作線程如何更新到「更新器」線程,知道「更新器」線程何時完成更新片中的指針?它應該只是睡覺並繼續檢查,還是有一個簡單的方法讓更新者發送消息回到推送到通道的特定線程?
UPDATE
我做了一個小測試腳本,看看是否試圖在同一時間訪問一個指針修改它......它似乎永遠是確定會發生什麼。沒有錯誤。我錯過了什麼嗎?
package main
import (
"fmt"
"sync"
)
type tester struct {
a uint
}
var things *tester
func updater() {
var a uint
for {
what := new(tester)
what.a = a
things = what
a++
}
}
func test() {
var t *tester
for {
t = things
if t != nil {
if t.a < 0 {
fmt.Println(`Error1`)
}
} else {
fmt.Println(`Error2`)
}
}
}
func main() {
var wg sync.WaitGroup
things = new(tester)
go test()
go test()
go test()
go test()
go test()
go test()
go updater()
go test()
go test()
go test()
go test()
go test()
wg.Add(1)
wg.Wait()
}
更新2
進一步考慮這一點,即使我讀,並在同一時間從多個線程寫入相同的變量...它沒有什麼區別,依然沒有任何錯誤:
從上面:
func test() {
var a uint
var t *tester
for {
t = things
if t != nil {
if t.a < 0 {
fmt.Println(`Error1`)
}
} else {
fmt.Println(`Error2`)
}
what := new(tester)
what.a = a
things = what
a++
}
}
這意味着我不必擔心併發在所有...再次:我失去了一些東西在這裏?
[沒有良性數據競賽](https://software.intel.com/zh-cn/blogs/2013/01/06/benign-data-races-what-could-possibly-go-wrong) !用賽跑探測器運行你的最後一個例子。只是因爲你無法引發錯誤或觀察到未定義的行爲,並不意味着它不會發生。你要麼有比賽,要麼沒有。 – JimB 2015-04-03 15:15:17
感謝您的鏈接,這很有趣!但事情是,只能有兩個結果,一個指向結構的指針,它總是包含相同的數據......或者無。在我的情況下,它是什麼都沒有區別。因此,只要指針過時,只有在發生實際的運行時錯誤時,根本不重要。 「nil指針解引用」,只有當指針實際上損壞時纔會發生。只要它始終是一個有效的指針(或零),那麼即使它已過期,我也沒有問題。 – Alasdair 2015-04-04 04:09:08