В чем разница между каналом и мьютексом в Golang? - PullRequest
0 голосов
/ 25 апреля 2020

Я слышал, что channel лучше, чем sycn.Mutex.Lock(), когда ваша программа имеет высокий параллелизм. Но почему канал более эффективен? По моему мнению, для реализации безопасного буферного пула (я думаю, что канал можно рассматривать как буферный пул), вы должны использовать блокировку.

И если channel более эффективен, почему существует sycn.Mutex? Потому что я могу написать код ниже, чтобы высмеивать sync.Mutex.

type seme struct {
    lock chan int
    locked bool
}


func (l *seme)Lock() {
// state 0 for initial, 1 for locked, 2 for free.
    if atomic.CompareAndSwapInt32(&l.state, 0, 1) {
        l.lock = make(chan int, 1)
    }
    l.lock <- 0
    l.state = 1
}

func (l *seme)UnLock() {
    if !atomic.CompareAndSwapInt32(&l.state, 1, 2)  {
        panic("UnLock a free Lock")
    }
    l.state = 2
    <- l.lock
}

Если channel лучше везде, чем mutex, почему я должен использовать mutex? То есть когда мне следует использовать mutex, а не channel? Может кто-нибудь привести пример?

Ответы [ 2 ]

2 голосов
/ 25 апреля 2020

Канал принципиально отличается от мьютекса.

Правильный ответ с достаточным количеством деталей будет слишком длинным, поэтому давайте просто рассмотрим основные моменты, в частности, в отношении Go каналов:

  • Канал Go обеспечивает типизированную передачу данных между параллельными подпрограммами (подпрограммами).
  • A sync.Mutex обеспечивает взаимное исключение из общей памяти между параллельные подпрограммы (goroutines).

Передача данных представляет собой копирование значения некоторого типа T. Goroutine A помещает значение в канал:

var v T  // v is a value of type T
...
ch <- v  // put v's value into the channel

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

Между тем, программа B извлекает значение из канала:

var w T  // w is also a value of type T
...
w <- ch

или просто:

w :=<- ch

Опять же, когда и будет ли это блокировать, то, что вы можете сделать, когда вы должны что-то сделать и т. Д. c., Может стать сложным; но в простом случае это ожидает доступного значения - какая-то процедура должна выполнить ch <- v, или уже сделала это, если канал буферизован - и затем копирует в переменную w значение, которое был помещен в канал. Переменная v могла измениться или даже полностью уничтожиться этой точкой. Значение было безопасно сохранено в канале, а теперь удалено из канала и помещено в переменную w.

Go Каналы имеют некоторые дополнительные функции, такие как возможность закрыть канал, что предотвращает дальнейшую запись на него и доставляет уведомления об окончании данных для операций чтения. Это можно проверить с помощью чтения одного значения (w, ok <- ch) и неявно протестировать в экземпляре for w := range ch l oop.

A sync.Mutex, напротив, просто позволяет вызвать Lock и Unlock. Он не содержит никаких значений в очереди (как буферизованный канал) и даже не имеет типа (кроме самого sync.Mutex), который удерживает вас от случайной отправки float чему-то ожидающему string или чему-либо еще. Наличие этой блокировки позволяет двум или более подпрограммам использовать областей совместно используемой памяти , чтобы что-то сделать.

Реализация среды выполнения канала, скорее всего, нуждается в каком-то виде мьютекса. Это не должно быть sync.Mutex само по себе: достаточно всего, что обеспечивает взаимное исключение. В реализации канала Go, которую вы, вероятно, используете , это не sync.Mutex, а специализированный исполняемый мьютекс . (Обратите внимание, что эта ссылка ведет на указанную c строку, и эта строка может со временем устареть.) Поскольку некоторый код канала напрямую генерируется самим компилятором, подпрограммы времени выполнения здесь не должны использоваться: Ваш компилятор может быть другим. Однако изучение этой конкретной реализации может немного рассказать вам о том, что вы можете делать с каналами.

Мьютексы, как правило, намного проще , чем каналы. Чтобы увидеть пример, сравните объем кода в вышеприведенной реализации канала, которая не включает встроенные вставки компилятора, с этой конкретной Go реализацией sync.Mutex исходного кода .

0 голосов
/ 25 апреля 2020

Существует два способа взаимодействия в параллельных программах в пакете Golang.

  1. syn c: общение путем совместного использования памяти.

  2. Каналы: делиться памятью, общаясь.

Go рекомендует

Не связываться, разделяя память. Вместо этого делитесь памятью, общаясь.

Какими операциями являются атомы c? А как насчет мьютексов? отвечает на этот вопрос

...