Безопасность потока - это простая концепция: безопасно ли выполнять операцию A на одном потоке, в то время как другой поток выполняет операцию B, которая может быть или не быть такой же, как операция A. Это может быть расширено для покрытия многих потоки. В этом контексте, «безопасный» означает:
- Нет неопределенное поведение
- Все инварианты структур данных гарантируется не наблюдается нитями
Фактические операции А и В имеют важное значение. Если два потока и читают a plain int
переменная, то это нормально. Однако, если какой-либо поток может записать эту переменную, и нет синхронизации, чтобы убедиться, что чтение и запись не могут выполняться вместе, тогда у вас есть гонка данных, которая является неопределенным поведением, и это не потокобезопасный.
Это относится в равной степени к сценарию, о котором вы просили: если вы не приняли специальные меры предосторожности, то небезопасно, чтобы один поток считывался из структуры одновременно с записью другого потока. Если вы можете гарантировать, что потоки не могут одновременно получить доступ к структуре данных, используя некоторую форму синхронизации, такую как мьютекс, критический раздел, семафор или событие, тогда нет проблем.
Вы можете использовать такие вещи, как мьютексы и критические разделы, чтобы предотвратить одновременный доступ к некоторым данным, так что поток записи является единственным потоком, который обращается к данным при его записи, а поток чтения - это единственный поток, который обращается к данным, когда он читает, тем самым предоставляя гарантию, о которой я только что упомянул. Поэтому это позволяет избежать неопределенного поведения, упомянутого выше.
Однако вам все же необходимо убедиться в том, что ваш код безопасен в более широком контексте: если вам нужно изменить несколько переменных, вам необходимо удерживать блокировку мьютекса на всей операции, а не на каждом отдельном доступе , в противном случае вы можете обнаружить, что инварианты вашей структуры данных могут не наблюдаться другими потоками.
Возможно также, что структура данных может быть потокобезопасной для некоторых операций, но не для других. Например, однопользовательская очередь с одним потребителем будет ОК, если один поток нажимает элементы в очереди, а другой выталкивает элементы из очереди, но прерывается, если два потока нажимают элементы, или два потока выталкивают элементы.
В примере, на котором вы ссылаетесь, дело в том, что глобальные переменные неявно разделяются между всеми потоками, и поэтому все обращения должны быть защищены некоторой формой синхронизации (например, мьютексом), если какой-либо поток может их модифицировать. С другой стороны, если у вас есть отдельная копия данных для каждого потока, то этот поток может изменить свою копию, не беспокоясь о параллельном доступе из любого другого потока, и синхронизация не требуется. Конечно, вам всегда нужна синхронизация, если два или более потока будут работать с одними и теми же данными.
Моя книга, C++ Concurrency in Action охватывает то, что означает, что вещи должны быть потокобезопасными, как создавать потокобезопасные структуры данных и примитивы синхронизации C++, используемые для этой цели, такие как std::mutex
.
Первые несколько хитов на [Google] (https://www.google.com/#hl=ru&sclient=psy-ab&q=c%2B%2B+thread-safe+programming&pbx=1&oq=c%2B%2B + потокобезопасный + программирование и водно = е & АКИ = & акль = & gs_sm = 3 & gs_upl = 532l4457l0l4950l27l12l0l2l2l0l481l3318l0.4.3.4.1l12l0 & gs_l = hp.3 ... 532l4457l0l4950l27l12l0l2l2l0l481l3318l0j4j3j4j1l12l0 & BAV = on.2, or.r_gc.r_pw.r_cp.r_qf., cf.osb & Fp = 3e688b5b28c62a4d & BIW = 1306 & bih = 867) кажутся весьма поучительными. – jogojapan
Извините, я пробовал это, я просто нашел его слишком сложным. Мне нужен простой пример и рабочий стол. Я думаю, – Kam
Почему два потока не могут получить доступ к структуре в одно и то же время? Если вы его не защищаете (например, семафором), вы не можете знать, записывает ли первый поток часть структуры, тогда планировщик ОС решает запустить поток, который читает структуру, которая теперь не будет полностью обновляться. –