# keylock Напишите примитив синхронизации, позволяющий "лочить" строки из множества. В обычном `sync.Mutex` лок всего один. В один момент времени этот лок может находиться у одной горутины. В нашем примитиве локов может быть сколько угодно. Каждый лок мы идентифицируем ключом - строкой. Каждая горутина приходит к нам со списком ключей и хочет захватить сразу все локи из этого списка. (Наша аналогия с `sync.Mutex` вовсе не значит, что нужно использовать `sync.Mutex` в реализации. Лучше использовать каналы, чтобы проще было реализовать отмену.) В итоге, внутри критической секции может быть сколько угодно горутин, но у всех них множества ключей попарно не пересекаются. Например, если к нам придёт 3 горутины: 1. `LockKeys({a, b})` 2. `LockKeys({b})` 3. `LockKeys({c})` То в критической секции одновременно могут находиться: * 1 и 3 * 2 и 3 А вот 1 и 2 одновременно в критическую секцию зайти не могут, потому что у них множества ключей пересекаются. Метод `LockKeys` должен блокироваться, пока не удастся захватить все ключи, либо пока операция не будет отменена через канал `cancel`. ```go package keylock type KeyLock interface { // LockKeys locks all keys from provided set. // // Upon successful completion, function guarantees that no other call with intersecting set of keys // will finish, until unlock() is called. // // If cancel channel is closed, function stops trying to lock received keys and returns immediately LockKeys(keys []string, cancel <-chan struct{}) (canceled bool, unlock func()) } ``` Реализация не должна содержать busy wait. То есть, если вызов LockKeys не может выполниться, потому что какие-то из ключей залочены другими горутинами, то текущая горутина должна засыпать.