2015-04-21 1 views
1

У меня есть требование обновить таймер общего объекта в нескольких процедурах go. Но в конечном итоге это состояние гонки. Я не могу использовать блокировку для ожидания на канале, потому что все остальные процедуры должны будут ждать.Несколько ходовых процедур, ожидающих общего таймера, вызывающего гонку

package main 
import( 
    "time" 
    "math/rand" 
) 
type R struct { 
    timer *time.Timer 
    //other fields 
} 

func f(done chan bool,r *R){ 
    r.timer =time.NewTimer(time.Millisecond * time.Duration(1000 + rand.Intn(2))) 
    //some code simultaneously accessing other fields of shared object r, cannot put a lock here 
    <-r.timer.C 

    done <- true 
} 

func main(){ 
    done := make(chan bool , 5) 
    var r *R 
    var t *time.Timer 
    r = &R{timer:t} 
    for i:=0;i<5;i++{ 
     go f(done,r) 
    } 
    for i:=0;i<5;i++{ 
     <-done 
    } 
} 

когда я бегу с помощью

go run -race thread.go 

это дает

================== 

WARNING: DATA RACE 
Write by goroutine 5: 
    main.f() 
     usr/local/gocode/thread.go:12 +0x69 

Previous write by goroutine 4: 
    main.f() 
     usr/local/gocode/thread.go:12 +0x69 

Goroutine 5 (running) created at: 
    main.main() 
     usr/local/gocode/thread.go:25 +0xd3 

Goroutine 4 (running) created at: 
    main.main() 
     usr/local/gocode/thread.go:25 +0xd3 
================== 

и вешает

любая помощь будет полезна

+3

Просто защитите параллельные записи в 'R.timer' с помощью' sync.Mutex', для чего они предназначены. – Volker

+0

даже если я эта гонка все еще происходит. между записью и ожиданием канала. тип Р { структура таймера * time.Timer // другие поля м sync.Mutex } FUNC F (сделано чан BOOL, R * R) { \t rmLock() r.timer = время. NewTimer (time.Millisecond * time.Duration (1000 + rand.Intn (2))) rmUnlock() // некоторый код, одновременно обращающийся к другим полям общего объекта r, не может помещать блокировку здесь <-r. timer.C done <- true } –

+0

Если вы не хотите использовать Mutex/locks, то идиоматическим способом является обмен информацией по каналам. Вы можете просто передать другой канал в свой метод и отправить значения, которые вы хотите обновить. После запуска goroutines у вас есть выбор или выбор в цикле, где вы обновляете свой экземпляр 'R' в неконкурентном контексте. – evanmcdonnal

ответ

2

Существует дизайн проблема здесь - вы иметь один объект R , и он имеет общий экземпляр, но каждый goroutine создает новый таймер, который является локальным. Мне кажется, что вам нужен локальный таймер на один горутин, и не делиться этим таймером между ними, это просто не имеет смысла.

Если переписать код так:

type R struct { 
    //other fields 
    Foo string 
    Bar interface{} 
} 

func f(done chan bool, r *R) { 
    timer := time.NewTimer(time.Millisecond * time.Duration(1000+rand.Intn(2))) 
    //some code simultaneously accessing other fields of shared object r, cannot put a lock here 
    <-timer.C 

    done <- true 
} 

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

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