Попытка приобрести замок с крайним сроком в Голанге? - PullRequest
0 голосов
/ 02 февраля 2019

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

Я могу вспомнить две ситуации прямо сейчас, где это было бы очень полезно и где я ищу какое-то решение.Первый из них: сервис с высокой нагрузкой на процессор, который получает запросы, чувствительные к задержке (например, веб-сервис)В этом случае вы захотите сделать что-то вроде примера RPCService ниже.Можно реализовать его как рабочую очередь (с каналами и прочим), но в этом случае становится все труднее измерить и использовать весь доступный ЦП.Также можно просто согласиться с тем, что к тому времени, когда вы получите блокировку, ваш код может быть уже истекшим сроком, но это не идеально, так как тратит некоторое количество ресурсов и означает, что мы не можем делать что-то вроде «деградированного ad-hoc».response ".

    /* Example 1: LockBefore() for latency sensitive code. */
    func (s *RPCService) DoTheThing(ctx context.Context, ...) ... {
      if s.someObj[req.Parameter].mtx.LockBefore(ctx.Deadline()) {
        defer s.someObj[req.Parameter].mtx.Unlock()
        ... expensive computation based on internal state ...
      } else {
        return s.cheapCachedResponse[req.Parameter]
      }
    }

Другой случай, когда у вас есть куча объектов, к которым нужно прикоснуться, но которые могут быть заблокированы, и когда касание их должно завершиться в течение определенного времени (например, обновление статистики).В этом случае вы также можете использовать LockBefore () или какую-либо форму TryLock () , см. Пример статистики ниже.

    /* Example 2: TryLock() for updating stats. */
    func (s *StatsObject) updateObjStats(key, value interface{}) {
      if s.someObj[key].TryLock() {
        defer s.someObj[key].Unlock()
        ... update stats ...
        ... fill in s.cheapCachedResponse ...
      }
    }

    func (s *StatsObject) UpdateStats() {
      s.someObj.Range(s.updateObjStats)
    }

Для простоты использования, давайте предположим, что в приведенном выше случае мы говорим о том же s.someObj .Любой объект может быть заблокирован операциями DoTheThing () на длительное время, что означает, что мы хотели бы пропустить его в updateObjStats .Кроме того, мы хотели бы убедиться, что мы возвращаем дешевый ответ в DoTheThing () в случае, если мы не можем получить блокировку во времени.

К сожалению, только sync.Mutexимеет функции Lock () и Unlock () . потенциально не может получить блокировку.Есть ли какой-нибудь простой способ сделать это вместо этого?Подхожу ли я к этому классу проблем с совершенно неправильной точки зрения, и есть ли другой, более «ходовой» способ их решения?Или мне придется реализовать собственную библиотеку Mutex, если я хочу решить их?Мне известно о выпуске 6123 , который, по-видимому, говорит о том, что такого не существует, и то, что я подхожу к этим проблемам, совершенно непросто.

Ответы [ 2 ]

0 голосов
/ 02 февраля 2019

Использовать канал с размером буфера один в качестве мьютекса.

l := make(chan struct{}, 1)

Блокировка:

l <- struct{}{}

Разблокировка:

<-l

Попробуйте блокировку:

select {
case l <- struct{}{}:
    // lock acquired
    <-l
default:
    // lock not acquired
}

Попробуйте с таймаутом:

select {
case l <- struct{}{}:
    // lock acquired
    <-l
case <-time.After(time.Minute):
    // lock not acquired
}
0 голосов
/ 02 февраля 2019

Я думаю, что вы спрашиваете здесь несколько разных вещей:

  1. Существует ли эта возможность в стандартной библиотеке?Нет, это не так.Вероятно, вы можете найти реализации в другом месте - это возможно для реализации с использованием стандартной библиотеки (например, atomics).

  2. Почему эта возможность не существует вСтандартная библиотека: вопрос, который вы упомянули в этом вопросе, является одним из обсуждений.Существует также несколько дискуссий по списку рассылки о чокнутых с участием нескольких разработчиков кода Go: ссылка 1 , ссылка 2 .И другие обсуждения легко найти, прибегая к помощи.

  3. Как мне разработать свою программу так, чтобы она мне не понадобилась?

ответ на (3) более нюансирован и зависит от вашей конкретной проблемы.Ваш вопрос уже говорит:

Возможно реализовать его как рабочую очередь (с каналами и прочим), но в этом случае становится все труднее измерить и использовать весь доступный ЦП

* 1027.*

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

В Go вам обычно нужны каналы всякий раз, когда схемы блокировки становятся нетривиальными.Это не должно быть медленнее, и это должно быть намного более ремонтопригодным.

...