О чем можно подумать, так как «состояние» вашего приложения может измениться, когда два разных потока вызовут одну и ту же функцию или взаимодействуют с одним и тем же объектом. В этом случае ваше «состояние» может быть «тем, что было написано до outStream
».
Рассмотрим следующий сценарий:
Thread 1 Thread 2
-------------------- -------------------------------
GetDetails(outStream, object1)
GetDetails(outStream, object2)
outStream.write(object1.detail1)
outStream.write(object2.detail1)
outStream.write(object1.detail2)
outStream.write(object2.detail2)
outStream.flush()
outStream.flush()
Две отдельные темы, как вызов GetDetails
, разделяя те же outStream
. Это потенциальная проблема параллелизма, поскольку данные, которые записываются в outStream
, не гарантируются в каком-либо конкретном порядке. Вы можете получить [object1.detail1, object2.detail1, object1.detail2, object2.detail2]
, или [object2.detail1, object1.detail1, object1.detail2, object2.detail2]
, и так далее.
GetDetails.apply
не изменяет состояние GetDetails
, но он изменяет состояние Writer
, которое вы передаете; чтобы обеспечить безопасность потоков, вы должны предпринять усилия, чтобы избежать одновременного использования одного и того же Writer (т. е. сценария выше).
В качестве контраргумента точки, здесь довольно ип резьбовых безопасный метод:
object NotThreadSafe {
// mutable state
private var currentPrefix = ""
def countUp(prefix: String) = {
// red flag: changing mutable state, then referring to it
currentPrefix = prefix
for(i <- 1 to 5) println(s"$currentPrefix $i")
}
}
Если нить 1 вызовы NotThreadSafe.countUp("hello")
и поток 2 вызовов NotThreadSafe.countUp("goodbye")
, выход будет зависеть от того, currentPrefix = prefix
бывает последним.
Вы могли бы в конечном итоге с
привет 1
привет 2
прощай 3 // должно быть привет 3, но currentPrefix переоделся
прощай 1
распрощаться 4 // должно быть привет 4
до свидания 5 // должно быть привет 5
до свидания 2
до свидания 3
до свидания 4
до свидания 5
или один из многих других перестановок.
Это одна из причин, почему Scala предпочитает «неизменность» и «безгражданство», потому что это инструменты, которые просто не нужно беспокоиться об этой проблеме.
Если вы вынуждены иметь дело с изменчивым состоянием, часто самый простой способ обеспечить безопасность потока - обеспечить, чтобы ваш метод мог вызываться только один раз за раз, то есть с помощью . На более тонком уровне вы хотите убедиться, что определенная последовательность шагов не чередуется с другой копией этой последовательности в другом потоке (например, сценарий GetDetails, который я описал в начале).
Вы также можете посмотреть semaphores, но это выходит за рамки этого ответа.
До тех пор, пока входы различаются (и не подключены к тому же потоку внизу), вы в порядке. – Dima