Ответ 1
Нет, добавочные значения в C не гарантируются как атомарные. Вам необходимо обеспечить синхронизацию или использовать библиотеку, предназначенную для системы, для выполнения атомных приращений/декрементов.
Я нашел код в FreeRTOS (FreeRTOSV7.4.0\FreeRTOS\Source\tasks.c):
void vTaskSuspendAll( void )
{
/* A critical section is not required as the variable is of type
portBASE_TYPE. */
++uxSchedulerSuspended;
}
Явным образом сказано, что нет необходимости защищать из-за типа "portBASE_TYPE", который является "длинным" типом. Я понял, что он предполагает, что самоинстрим этого типа является атомарным. Но после того, как я разобрал его, я не смог найти никаких доказательств, это простой файл load- > add- > . Тогда это проблема?
void vTaskSuspendAll( void )
{
/* A critical section is not required as the variable is of type
portBASE_TYPE. */
++uxSchedulerSuspended;
4dc: 4b03 ldr r3, [pc, #12] ; (4ec <vTaskSuspendAll+0x10>)
4de: f8d3 2118 ldr.w r2, [r3, #280] ; 0x118
4e2: 1c50 adds r0, r2, #1
4e4: f8c3 0118 str.w r0, [r3, #280] ; 0x118
4e8: 4770 bx lr
4ea: bf00 nop
4ec: 00000000 .word 0x00000000
000004f0 <xTaskGetTickCount>:
return xAlreadyYielded;
}
Нет, добавочные значения в C не гарантируются как атомарные. Вам необходимо обеспечить синхронизацию или использовать библиотеку, предназначенную для системы, для выполнения атомных приращений/декрементов.
Это не атомный, как вы документировали. Но он все равно может быть "потокобезопасным" в менее строгом смысле: long
не может находиться в противоречивом состоянии. Степень опасности здесь состоит в том, что если n
потоки вызывают vTaskSuspendAll
, тогда uxSchedulerSuspended
будет увеличиваться где угодно между 1 и n
.
Но это может быть совершенно нормально, если переменная - это то, что не обязательно должно быть совершенным, например трекер, сколько раз пользователь просил приостановить действие. Там "thread safe" означает "эта операция дает тот же результат, независимо от того, как его вызовы чередуются", и там "потокобезопасный" означает "ничего не взрывается, если вы вызываете это из нескольких потоков".
Операция не является атомарной, но нигде она не говорит об этом. Тем не менее, код является потокобезопасным, но вам нужно будет хорошо знать, что делает код, и как он вписывается в дизайн планировщика, чтобы это знать. Не имеет значения, изменяют ли другие задачи переменную между загрузкой и хранилищем, потому что, когда выполняемая последующая задача запускается, она будет находить переменную в том же состоянии, что и при первоначальной загрузке (поэтому части изменения и записи по-прежнему являются согласованными и действительными).
Как и в предыдущих опубликованных заметках, long не может находиться в противоречивом состоянии, потому что это базовый тип архитектуры, на которой он запущен. Однако подумайте, что произойдет, если код будет работать на 8-битной машине (или 16 бит), а переменная будет 32 бит. Тогда он не был бы потокобезопасным, потому что полные 32 бита были бы изменены байтом или словом за раз, а не все одновременно. В этом случае один байт может быть загружен в регистр, изменен, а затем записан обратно в ОЗУ (оставив остальные три байта без модификации) при переключении контекста. Если следующая выполняемая задача прочитает одну и ту же переменную, она будет читать один байт, который был изменен, и три байта, которые не были - и у вас есть большая проблема.
С уважением, Ричард (http://www.FreeRTOS.org)