«избирательное» взаимное исключение по горутинам - PullRequest
0 голосов
/ 05 февраля 2019

Я новичок в Go, и я хотел бы реализовать собственный механизм взаимного исключения, при котором каждый пользователь приложения может выполнять одну процедуру за раз.Чтобы упростить, рассмотрим U1 и U2 как пользователей приложения, а F1 (userId), F2 (userId) и F3 (userId) три различные программы, которые читают / записывают записи из базы данных , которые относятся только к данному пользователю .Мне бы хотелось, чтобы, если вызовы U1 (F1, F2, F3), (F2, F3) не выполнялись до тех пор, пока не завершится F1, выполняются F2 или F3 (в порядке вызова будет оптимальное решение), наконец, оставшийсявыполнен.Блокировка U1 не влияет на U2, но она управляется собственной блокировкой, как описано выше.Как бы вы это реализовали?Есть ли встроенный примитив?Спасибо

Ответы [ 2 ]

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

Используйте sync.Mutex для взаимного исключения.Используйте один мьютекс для каждого активного пользователя.

Я предполагаю, что пользователи идентифицируются по какому-то идентификатору.Давайте назовем этот тип userID.Используйте карту с ключом userID для хранения мьютекса на пользователя.

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

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

Вот код:

type userID string // replace userID type with id type for your application

// userLock is the per user lock
type userLock struct {
    sync.Mutex

    // n is number of goroutines between lock and unlock
    n int
}

var (
    // mu protects locks map and n field in userLock
    mu    sync.Mutex

    // locks is map of active locks
    locks = map[userID]*userLock{}
)


// lockUser locks the user specified by id. If the lock is
// already in use, the calling goroutine blocks until
// the lock is available.
func lockUser(id userID) {
    mu.Lock()
    l := locks[id]
    if l == nil {
        l = &userLock{}
        locks[id] = l
    }
    l.n++
    mu.Unlock()
    l.Lock()
}

// unlockUser unlocks the user specified by id. It 
// is a run-time error if the user is not locked on
// entry unlockUser.
func unlockUser(id userID) {
    mu.Lock()
    l := locks[id]
    if l == nil {
        panic("unlock without lock")
    }
    l.n--
    if l.n == 0 {
        delete(locks, id)
    }
    mu.Unlock()
    l.Unlock()
}
0 голосов
/ 05 февраля 2019

Вы можете использовать буферизованный канал в качестве семафора и создать такой канал для каждого пользователя.Тогда две программы, относящиеся к одному и тому же пользователю, смогут делать что-то только после получения семафора (успешной записи в канал).


Ресурсы по использованию каналов Go в качестве семафоров:

...