Ответ 1

Вы используете его в любое время, когда хотите записать данные в память после выполнения какой-либо работы и убедитесь, что другой поток не перезаписал пункт назначения с момента его запуска. Многие алгоритмы lock/mutex-free принимают эту форму.

Ответ 2

Хорошим примером является "приращение".

Произнесите два потока: a = a + 1. Скажем a начинается со значения 100. Если оба потока работают одновременно (многоядерные), оба будут загружать a как 100, увеличивать до 101 и сохранять это обратно в a. Неправильно!

С помощью test-and-set вы говорите: "Установите a в 101, но только если в настоящее время оно имеет значение 100." В этом случае один поток пройдет этот тест, а другой не удастся. В случае сбоя поток может повторить весь оператор, на этот раз загружая a как 101. Успех.

Это обычно быстрее, чем использование мьютекса, потому что:

  • В большинстве случаев нет условия гонки, поэтому обновление происходит без необходимости получения какого-либо мьютекса.
  • Даже при столкновении один поток не блокируется вообще, и быстрее для другого потока просто вращаться и повторять попытку, чем было бы приостановить себя в строке для некоторого мьютекса.

Ответ 3

Представьте, что вы писали банковское выражение, и ваша заявка имела просьбу снять со счета десять фунтов (да, я английский)). Поэтому вам нужно прочитать текущий баланс счета в локальной переменной, вычесть изъятие, а затем записать баланс обратно в память.

Однако, что, если между вами просматривается значение, и вы его записываете, возникает параллельный запрос? Там вероятность того, что результат этого запроса будет полностью перезаписан первым, и баланс учетной записи будет неправильным.

Test-and-set помогает нам исправить эту проблему, проверив, что значение вашей перезаписи - это то, что, по вашему мнению, должно быть. В этом случае вы можете проверить, что баланс был исходным значением, которое вы читали. Поскольку он является атомарным, он не прерывается, поэтому никто не может вытащить ковер из-под вас между чтением и записью.

Другой способ устранить ту же проблему - снять блокировку в ячейке памяти. К сожалению, блокировки чрезвычайно трудно получить правильно, трудно рассуждать, иметь проблемы с масштабируемостью и плохо себя вести в случае сбоев, поэтому они не являются идеальным (но определенно практичным) решением. Подходы, основанные на тестах и ​​наборах, составляют основу некоторых программных транзакционных воспоминаний, которые оптимистично позволяют каждой транзакции выполнять одновременно, за счет того, что они перевернут их обратно, если они конфликтуют.

Ответ 4

В основном, его использование именно для мьютексов, учитывая огромное значение атомарности. Что это.

Test-and-set - это операция, которая может выполняться с двумя другими инструкциями, неатомными и более быстрыми (атомарность несет аппаратные издержки при использовании многопроцессорных систем), поэтому обычно вы не будете использовать ее по другим причинам.

Ответ 5

Он используется, когда вам нужно получить общее значение, сделать что-то с ним и изменить значение, если другой поток еще не изменил его.

Что касается практических применений, то в последний раз я видел это в реализациях параллельных очередей (очередей, которые могут быть вытолкнуты/выведены несколькими потоками без необходимости семафоров или мьютексов).

Почему вы используете TestAndSet вместо мьютекса? Потому что обычно требуется меньше накладных расходов, чем мьютекс. Если для мьютекса требуется вмешательство ОС, TestAndSet может быть реализован как одна атомная команда на CPU. При работе в параллельных средах с 100 потоками один мьютекс в критическом разделе кода может вызвать серьезные узкие места.