У меня в классе есть такой метод:
class User < ApplicationRecord
...
def answers
@answers ||= HTTParty.get("http://www.example.com/api/users/#{self.id}/answers.json")
end
...
end
Поскольку я использую Puma в качестве веб-сервера, мне интересно, является ли этот код потокобезопасным? может кто-нибудь подтвердить это и, если возможно, объяснить, почему это потокобезопасно?
Это в методе экземпляра, не путать с методом класса. Метод answers
находится в примерUser
, а не в самом классе User
. Этот метод кэширует ответы в экземпляре User
, но пока этот экземпляр User
создается с каждым веб-запросом (например, User.find()
или User.find_by()
), все в порядке, потому что экземпляр не живет между потоками. Обычной практикой является поиск записей каждого веб-запроса в контроллере, так что вы, вероятно, делаете это.
Если бы этот метод был непосредственно в классе User
(например, User.answers
), тогда вам нужно было бы оценить, безопасно ли сохранение этого кэшированного значения в потоках и веб-запросах.
Напомним, ваша единственная забота о безопасности потоков - это методы класса, переменные класса (переменные экземпляра, которые используют два знака at, такие как @@answers
) и методы экземпляра, в которых экземпляр живет после одного веб-запроса.
Если вам когда-либо понадобится безопасно использовать переменную уровня класса, вы можете использовать Thread.current
, который по сути является хешем для каждого потока (например, {}), в котором вы можете хранить значения. Например, Thread.current[:foo] = 1
может быть примером. ActiveSupport использует это при настройке Time.zone
.
В качестве альтернативы вы можете найти моменты, когда вам понадобится один массив, который вам нужно безопасно разделить между потоками, и в этом случае вам нужно будет изучить Mutex
, который в основном позволяет вам иметь массив, который вы блокируете и разблокируете, чтобы предоставить потокам безопасный доступ к читать и писать в нем. Гем Sidekiq, например, использует Mutex для управления рабочими. Вы блокируете мьютекс, чтобы никто другой не мог его изменить, затем вы пишете в него, а затем разблокируете его. Важно отметить, что если какой-либо другой поток хочет записать в мьютекс, пока он заблокирован, ему придется подождать, пока он не станет разблокированным (например, поток просто приостанавливается, пока другой поток пишет), поэтому важно заблокировать как как можно короче.
Спасибо за ответ. Как вы проверяете, действует ли метод экземпляра после одного веб-запроса?
@Crashalot Чтобы экземпляр объекта можно было использовать в нескольких веб-запросах (что потенциально небезопасно), вам необходимо использовать переменную класса, переменную экземпляра уровня класса, константу или локальную переменную потока. Вероятно, было бы проще, если бы вы просто разместили вопрос с соответствующим кодом.
Исходя из моих ограниченных знаний о
puma
, отдельные потоки puma не используют один и тот же экземплярuser
. Следовательно, будет выполняться отдельный запрос GET в каждом потоке puma