2013-08-08 12 views
6

Próbuję zrozumieć, co się dzieje, gdy uzyskujesz równoczesny dostęp do metod wskaźników?Przejdź do współbieżnego dostępu do metod wskaźników

Mam mapę wskaźników i spawn off kilka procedur go. Przechodzę mapę do każdego rutynowego programu, a każda rutynowa procedura używa jednej z wartości na mapie. Nic nie jest zapisywane na mapie tylko odczytywane.

Mapa jest małe, tylko 4 przyciski więc jest możliwe, że więcej niż jeden przejdź rutynowe będzie przy użyciu tej samej wartości z mapy.

Pytanie jest, co się dzieje, gdy Two Go procedury wywołać metodę z tego samego wskaźnika? Czy otrzymam nieprzewidywalne wyniki?

EDIT

Przykład: Biorę się fragment mapy jak to nie jest pytanie jestem po.

Mam foo, który jest wskaźnikiem typu MyStruct i ta struktura ma metodę DoSomething, która pobiera argumenty. W funkcji main tworzę dwa go routines i oba wywołują foo.DoSomething przekazując różne wartości. W tym przykładzie, pierwsza rutynowa procedura ma znacznie większe obliczenia niż preforma (wystarczy użyć tutaj godzin snu do symulacji obliczeń). Znowu nic w strukturze się nie zmienia. Wezwę tylko metodę konstrukcji. Czy muszę się martwić drugim rutynowym nawiązywaniem połączenia z numerem foo.DoSomething, gdy pierwsza procedura go działa nadal działa z tą metodą?

package main 

import (
    "log" 
    "time" 
) 

type MyStruct struct { 
} 

func (self *MyStruct) DoSomething(value int) { 

    log.Printf("%d Start", value) 

    calculation_time := time.Duration(value) * time.Second 
    log.Printf("%d Calculating", value, calculation_time) 
    time.Sleep(calculation_time) 

    log.Printf("%d Done", value) 
} 

func main() { 

    var foo = new(MyStruct) 

    go foo.DoSomething(5) 

      // is this method call a problem when the first one is still working? 
    go foo.DoSomething(2) 

    time.Sleep(time.Duration(6 * time.Second)) 
} 

Odpowiedz

7

Metody Go mają odbiorniki. Receiver może być typem wskaźnika.Sposób z podpisem, na przykład:

func (r *R) foo(bar baz) // A method 

jest same jak

func foo(r *R, bar baz) // A plain old function 

Innymi słowy, odbiornik, wyżeł, czy nie, jest tylko gniazdo argumentem. Twoje pytanie teraz zmniejsza się do:

Co się stanie, gdy dwie procedury go wywołają powyższą funkcję o tej samej wartości r?

Odp .: To zależy. Konfiguracje problemów:

  • foo nie jest ponownie wprowadzany z żadnego powodu.
  • foo mutuje *r (pointee) bez koordynacji/synchronizacji.
  • Ostatni punkt jest tylko szczególnym przypadkiem: Jeśli foo mutuje jakikolwiek wspólny stanu w/o koordynacji/synchronizacji: niczego może się zdarzyć.

Jeśli foo unika powyższe doły smoły następnie to bezpieczne dla wykonywane są jednocześnie przez wielu goroutines nawet o tej samej wartości R.

2

Każdy wskaźnik jest uważany za bezpieczny dla wątków. Procedura rutyny powinna być traktowana jako osobny wątek, nawet jeśli tak nie jest. Procedury Go są multipleksowane na wątkach os.

Jeśli wartość jest zawsze tylko do odczytu (nigdy się nie zmieni), można odczytać z tak wielu przejść procedury, jak chcesz. Jak tylko zmienisz wartość, otrzymasz niespójne wyniki.

Aby zsynchronizować dostęp i uniknąć problemów (i potencjalnych paniki) należy użyć sync.RWMutex. Więc zamiast bezpośrednio czytać/pisać, używasz funkcji gettera i setera. Program pobierający użyje m.RLock() i m.RUnlock(). Seter użyje m.Lock() i m.Unlock().

Podczas korzystania z muteksów spróbuj odblokować tak szybko, jak to możliwe. Zachować kod pomiędzy zamkiem i odblokować jak najkrótsze:

m.Lock() 
// Do what you need to do for the lock 
mymap[key] = value 
m.Unlock() 
// Do everything else here 

sync.RWMutex różni się od sync.Mutex w to, że pozwala, aby mieć jak wiele jednoczesnych czytelników, jak chcesz (RLock oznacza odczytu-lock). Gdy tylko autor próbuje zablokować, uniemożliwia innym czytelnikom uzyskanie blokady i czeka, aż wychodzący czytelnicy zwolnią blokady.

Można również użyć kanałów do przekazywania wartości między procedurami go. Kanały działają w wielu sytuacjach i są zachęcane. Więcej informacji na temat współbieżności można znaleźć pod adresem Effective Go. Kanały nie zawsze pasują do każdej sytuacji, więc zależy to od sytuacji.

+0

Dziękuję za poświęcony mi czas, ale nie jest to dokładnie to, o co prosiłem. Dodałem edycję z kodem przykładu, do którego próbuję się dostać. Przepraszamy za jakiekolwiek zamieszanie. – Jeff

+0

@Jeff Tak więc moja odpowiedź brzmi: "Jeśli wartość jest zawsze tylko do odczytu (nigdy się nie zmieni), możesz czytać z wielu procedur, jak chcesz.". Nie musi być specyficzna dla mapy - jest wskaźnikiem. To samo dotyczy. – Luke

1

Otrzymujesz stan wyścigu za każdym razem, gdy ktoś modyfikuje zmienną, podczas gdy ktoś inny ją czyta. W twoim przykładzie zmienna foo/self jest odczytywana przez wiele goroutinów jednocześnie, ale ponieważ nikt nie modyfikuje jej podczas jednoczesnego dostępu, wszystko jest w porządku.

Bardziej interesującym przykładem może być struktura MyStruct z dodatkowymi atrybutami, np. attribute1. W takim przypadku te same zasady obowiązują również dla tego atrybutu. Możesz czytać go z różnych goroutinów jednocześnie - na przykład twoja metoda DoSomething może wydrukować również wartość self.attribute1 - ale nie możesz jej modyfikować w tym czasie.

Jeśli chcesz mieć możliwość modyfikowania zmiennej podczas jej uzyskiwania, potrzebujesz jakiegoś prymitywu synchronizacji. Podejście idiomatyczne Go polegałoby na uniknięciu jednoczesnego dostępu do tej samej zmiennej, przy użyciu tylko zmiennych lokalnych, które są dostępne za pośrednictwem pojedynczej goroutine i komunikują się za pośrednictwem kanałów, kiedy potrzebujesz danych z innej goroutine.

Alternatywne podejścia, które są również dostępne w Go, są elementami pierwotnymi z pakietu sync, takimi jak sync.Mutex. Pakiet sync/atomic oferuje również więcej prymitywów prymitywnych, które mogą być używane do ładowania/zapisywania/modyfikowania/porównywania i zamiany zmiennych atomowo.

Powiązane problemy