Как правило, потоки могут работать на разных процессорах / ядрах с разными кэшами памяти. Они могут работать на одном и том же ядре с одним прерыванием («вытеснение» другого). Это имеет два последствия:
1) У вас нет возможности узнать, будет ли один поток прерываться другим в процессе выполнения чего-либо. Таким образом, в вашем примере нет никакого способа быть уверенным, что thread1 не будет пытаться прочитать строковое значение до того, как thread2 его запишет, или даже что, когда thread1 его читает, оно находится в «согласованном состоянии». Если он не находится в согласованном состоянии, то его использование может сделать что угодно.
2) Когда вы пишете в память в одном потоке, невозможно сказать, увидит ли это изменение код, запущенный в другом потоке. Изменение может находиться в кэше потока записи и не быть сброшенным в основную память. Он может быть сброшен в основную память, но не попадет в кэш потока чтения. Часть изменений может произойти, а часть - нет.
Как правило, без блокировок (или других механизмов синхронизации, таких как семафоры) вы не можете сказать, произойдет ли что-то, что происходит в потоке A, «до» или «после» чего-то, что происходит в потоке B. У вас также нет способ сказать, будут ли изменения, сделанные в потоке A, «видны» в потоке B.
Правильное использование блокировки гарантирует, что все изменения сбрасываются через кэши, поэтому код видит память в том состоянии, которое, как вы думаете, он должен видеть. Это также позволяет вам контролировать, могут ли отдельные биты кода выполняться одновременно и / или прерывать друг друга.
В этом случае, глядя на ваш код выше, минимальная блокировка, в которой вы нуждаетесь, должна иметь примитив синхронизации, который освобождается / публикуется вторым потоком (писателем) после того, как он записал строку, и получил / ждал от первый поток (читатель) перед использованием этой строки. Это тогда гарантировало бы, что первый поток видит любые изменения, сделанные вторым потоком.
Предполагается, что второй поток не запускается до тех пор, пока не будет вызван firstFunctionRunFromThread1. Если это не так, то вам нужно одно и то же с записью потока 1 и чтением 2.
Самый простой способ сделать это - создать мьютекс, который "защищает" ваши данные. Вы сами решаете, какие данные вы защищаете, и любой код, который читает или записывает данные, должен содержать мьютекс, пока он это делает. Итак, сначала вы блокируете, затем читаете и / или записываете данные, затем разблокируете. Это гарантирует непротиворечивое состояние, но само по себе не гарантирует, что thread2 получит возможность что-либо сделать вообще между двумя различными функциями thread1.
Любой тип механизма передачи сообщений также будет включать в себя необходимые барьеры памяти, поэтому, если вы отправите сообщение из потока писателя в поток читателя, означающее «Я закончил писать, вы можете читать сейчас», тогда это будет быть правдой.
Могут быть более эффективные способы выполнения определенных действий, если они оказываются слишком медленными.