2015-05-20 2 views
-1

Я использую this как concurrentmap и буферные каналы в качестве значения карты для потокобезопасности (работает как очередь), когда тест использует 10 goroutines, значение, полученное от канала, было не таким же один отправить, любое предложение?golang thread safe map с каналом как значения для потокобезопасности

package main 

import "fmt" 
import "github.com/streamrail/concurrent-map" 

func main() { 
    testmap := cmap.New() 
    fmt.Println("SyncMapNew: ", TestInParallel(&testmap, 10)) 
} 

func TestInParallel(g *cmap.ConcurrentMap, n int) time.Duration { 
    start := time.Now() 
    var wait sync.WaitGroup 

    for i := 0; i < n; i++ { 
     wait.Add(1) 
     go func() { 
      TheTest(g, rand.New(rand.NewSource(int64(i*500)))) 
      wait.Done() 
     }() 
    } 
    wait.Wait() 
    return time.Now().Sub(start) 
} 

func TheTest(g *cmap.ConcurrentMap, rnd *rand.Rand) time.Duration { 
    start := time.Now() 
    var key string 
    var value time.Time 
    for i := 0; i < 10000; i++ { 
     key = strconv.Itoa(int(rnd.Int31n(50000))) 
     if g.Has(key) == false { 
      g.Set(key, make(chan time.Time, 100)) 
     } 
     tchan, _ := g.Get(key) 
     castchan := tchan.(chan time.Time) 
     value = time.Now() 
     castchan <- value 
     got := <-castchan 
     g.Set(key, castchan) 
     if value != got { 
      panic(fmt.Sprintf("ERROR: expected %v, got %v", value, got)) 
     } 
    } 
    return time.Now().Sub(start) 
} 

обновление я неправильно понять бизнес-логику, код должен нравится этот

key = strconv.Itoa(int(rnd.Int31n(500))) 
tchan, _ := g.GetSet(key, make(chan time.Time, 100)) 
castchan := tchan.(chan time.Time) 
value = time.Now() 
if len(castchan) >= 99 { 
    <-castchan//do somthing here 
} 
castchan <- value 
g.Set(key, castchan) 
+0

http://play.golang.org/p/RqxEjTei2e – sdhjl2000

+0

Учитывая недостатки в пакете «concurent-map», который вы используете (некоторые из них упоминаются в [ответе Дэвида Будворта] (http://stackoverflow.com/) a/30339509/55504), есть больше), и вы легко можете добавить 'sync.Mutex', который блокирует то, что вам действительно нужно (что часто немного больше, чем отдельные обращения к карте). Я не вижу причин использовать пакет. –

ответ

0

Вы используете случайные ключи, так что больше, чем один goroutine может получить такое же случайное число.

если две подпрограммы получают одинаковое количество в то же (МОГ) время, затем

Это состояние гонки:

if g.Has(key) == false { 
    g.Set(key, make(chan time.Time, 100)) 
} 

Вполне возможно, что h.Has ложно для двух goroutines в то же время, а затем они оба набора, так goroutine1 наборы чан а, и goroutine2 устанавливает чан B, а затем и в конечном итоге с помощью чан B

чтобы решить эту проблему, вам нужно что-то вроде

SetIfAbsent 

Какие блокировки, проверяет, существует ли он, а если нет, устанавливает, а затем разблокирует.

Библиотека, с которой вы связали карту, не очень полезна в качестве кеша, поскольку она не предоставляет атомную функцию типа SetIfAbsent.


В случае нет гонки g.Has/g.Set, а затем, если две процедуры просто так, чтобы получить тот же ключ, и, таким образом, тот же канал, вы не гарантированы, какое значение сначала в очереди и который читается первым.

поэтому goroutine1 может прочитать значение goroutine2, вставленное или наоборот.

Когда вы думаете об использовании общего состояния в одновременно выполняющейся системе, вы должны предположить, что любая другая операция может произойти между любыми операторами/строками кода.

Мне часто нравится думать об этом, вы должны предположить, что каждая строка вашего кода запускается на каждом ядре по одному.

так, в Has/Set примера это будет:

if g.Has(key) == false { // goroutine1 
if g.Has(key) == false { // goroutine2 
    g.Set(key, make(chan time.Time, 100)) //goroutine1 
    g.Set(key, make(chan time.Time, 100)) //goroutine2 
} //goroutine1 
} //goroutine2 
tchan, _ := g.Get(key) //goroutine1 
tchan, _ := g.Get(key) //goroutine2 

увидеть, где ошибка есть? вторая рутина помещает его в карту, но оба извлекают тот же канал на линии tchan.

иметь смысл?

+0

привет, я знаю ситуацию, которую вы описываете, поэтому я пытаюсь найти и написать безопасную по потоку карту, я тестирую с помощью этого https://gist.github.com/craigmj/5770480, но все три структуры медленны. – sdhjl2000

+0

Вы имеете в виду продолжать повторное использование одних и тех же ключей снова и снова?Вы выполняете итерации 10k всего за 500 ключей (так что они будут сталкиваться много). Какой фактический вариант использования вы пытаетесь оптимизировать? Ваш «набор и немедленное чтение» делает определенные виды оптимизации невозможными (вы не можете выполнять пакетную запись). Это ваше ожидаемое использование? Какие ключи вы действительно хотите использовать? Сколько? Сколько памяти вы готовы потратить на производительность (с достаточной памятью, вы можете использовать массив, а не карту)? Действительно ли клавиши являются случайными (есть преимущества, которые мы можем использовать там)? Случаи использования имеют значение. –

+0

@ RobNapier, ключ и значение нужно повторно использовать в реальном состоянии, почтовый код - это просто проверить работу канала как очередь FIFO, в конце концов, я понимаю, что я пропустил понимание бизнес-логики – sdhjl2000