2013-09-18 4 views
7

Java предоставляет очень удобную идиому для синхронизации критических участков кода:Что такое golang-эквивалент Java synchronized() block?

synchronized(someObject) { 
    // do something really important all by myself with nobody bothering me 
} 

Или

public synchronized void doSomething() { 
    // ... 
} 

Что такое идти эквивалент?

(Быстрый поиск показывает: golang.org/pkg/sync/ - который, кажется (возможно я ошибаюсь) слишком низкий уровень для общего пользования.)

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

ответ

8

sync.Mutex является взаимной блокировкой исключения, он может обеспечивать аналогичную функциональность для ключевого слова java ключевого слова synchronized (за исключением того, что блокировки в java обеспечивают reentrant взаимное исключение):

synchronized(someObject) { 
    // 
} 

эквивалентно:

var l sync.Mutex 

l.Lock() 
// 
l.Unlock() 
+0

Семантика не то же самое. Синхронизированные блоки Java обеспечивают блокировки повторного входа. –

+0

Да, вы правы @ Анонимно, но 'Go' не имеет' java-like 'класса наследования, поэтому я считаю, что нет необходимости в« реентерабельных блокировках »в' Go'. – tarrsalah

+4

http://play.golang.org/p/jfeBxkve65 будет работать на Java, но тупики в Go. Наследование не играет роли. –

6

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

Полный пример в этом стиле выглядит следующим образом. Интересный код находится в FanOuter.

package main 

import (
    "fmt" 
    "time" 
) 

type Message int 

type ListenerUpdate struct { 
    Add  bool 
    Listener chan Message 
} 

// FanOuter maintains listeners, and forwards messages from msgc 
// to each of them. Updates on listc can add or remove a listener. 
func FanOuter(msgc chan Message, listc chan ListenerUpdate) { 
    lstrs := map[chan Message]struct{}{} 
    for { 
     select { 
     case m := <-msgc: 
      for k := range lstrs { 
       k <- m 
      } 
     case lup := <-listc: 
      if lup.Add { 
       lstrs[lup.Listener] = struct{}{} 
      } else { 
       delete(lstrs, lup.Listener) 
      } 
     } 
    } 
} 

func main() { 
    msgc := make(chan Message) 
    listc := make(chan ListenerUpdate) 
    go FanOuter(msgc, listc) 
    // Slowly add listeners, then slowly remove them. 
    go func() { 
     chans := make([]chan Message, 10) 
     // Adding listeners. 
     for i := range chans { 
      chans[i] = make(chan Message) 
      // A listener prints its id and any messages received. 
      go func(i int, c chan Message) { 
       for { 
        m := <-c 
        fmt.Printf("%d received %d\n", i, m) 
       } 
      }(i, chans[i]) 
      listc <- ListenerUpdate{true, chans[i]} 
      time.Sleep(300 * time.Millisecond) 
     } 
     // Removing listeners. 
     for i := range chans { 
      listc <- ListenerUpdate{false, chans[i]} 
      time.Sleep(300 * time.Millisecond) 
     } 
    }() 
    // Every second send a message to the fanouter. 
    for i := 0; i < 10; i++ { 
     fmt.Println("About to send ", i) 
     msgc <- Message(i) 
     time.Sleep(1 * time.Second) 
    } 
} 
+0

Это действительно хороший ответ, так как он не просто переводит блокировку из java для перехода, она использует решение для устранения исходной проблемы, с которой вы сталкиваетесь при параллелизме. Он также четко отвечает на указанную проблему. +1 – Nikkolasg

5

, чтобы отменить ответ tarrsalah.

Вы можете добавить sync.Mutex к своему объекту, позволяя им напрямую блокировать и разблокировать.

type MyObject struct{ 
    Number int 
    sync.Mutex 
} 

func (m *MyObject)Increment(){ 
    m.Lock() 
    defer m.Unlock() 
    m.Number++ 
} 

Defer'd команды будут работать в конце функции, таким образом, вы знаете, он получает и заблокирован и разблокирован, в больших функций.

+1

Этот ответ лучше, потому что он упоминает о переносе. Вызов unlock() вручную зависит от взаимоблокировок – eshalev