static public function acquire($key, $options = [])
    {
        $keyMD5 = md5(self::$keyPrefix . serialize($key));
        $timeout = isset($options['timeout']) ? (float) $options['timeout'] : self::$defaultLockTimeout;
        $retryInterval = 0.5;
        $maxRetriesCount = floor($timeout / $retryInterval);
        $lock = function() use ($keyMD5) {
            if (!isset(self::$data[$keyMD5])) {
                set_error_handler(function($errno, $errstr) {
                    throw new \Exception($errstr);
                });
                $dir = self::getLocksDir();
                if (!is_dir($dir)) {
                    try {
                        mkdir($dir, 0777, true);
                    } catch (\Exception $e) {
                        if ($e->getMessage() !== 'mkdir(): File exists') { // The directory may be just created in other process.
                            restore_error_handler();
                            return false;
                        }
                    }
                }
                $filename = $dir . $keyMD5 . '.lock';
                try {
                    $filePointer = fopen($filename, "w");
                    if ($filePointer !== false && flock($filePointer, LOCK_EX | LOCK_NB) !== false) {
                        self::$data[$keyMD5] = $filePointer;
                        restore_error_handler();
                        return true;
                    }
                } catch (\Exception $e) {
                    
                }
                restore_error_handler();
                return false;
            }
            return false;
        };
        $startTime = microtime(true);
        for ($i = 0; $i < $maxRetriesCount + 1; $i++) {
            if ($lock()) {
                return;
            }
            if (microtime(true) - $startTime > $timeout) {
                break;
            }
            usleep($retryInterval * 1000000);
        }
        throw new \Exception('Cannot acquire lock for "' . $key . '"');
    }