2013-03-20 8 views
13

ich einige Code in FreeRTOS (FreeRTOSV7.4.0 \ FreeRTOS \ Source \ tasks.c) gefunden:Ist die Selbstinkremente in C threadsicher?

void vTaskSuspendAll(void) 
{ 
    /* A critical section is not required as the variable is of type 
    portBASE_TYPE. */ 
    ++uxSchedulerSuspended; 
} 

Es wird ausdrücklich gesagt, keine Notwendigkeit, aufgrund der Art zu schützen ist "portBASE_TYPE", die ein " lang "tippen. Mein Verständnis ist, dass es davon ausgeht, dass das Selbstinkrement zu diesem Typ atomar ist. Aber nachdem ich es auseinander genommen habe, konnte ich keinen Beweis finden, es ist eine einfache Last-> hinzufügen-> speichern. Dann ist es ein Problem?

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; 
} 
+7

Es definiert ist nicht von C. Es hängt von der Compiler/Hardware-Plattform. –

+1

Eine gute Politik zu übernehmen, wenn sie mit Thread-Sicherheit zu tun, ist dies: Wenn es auch nur die kleinste Spur einer Chance, dass eine Race-Bedingung auftritt, treten sie dann. Dies beantwortet Ihre Frage nicht, aber es ist nützlich, sich zu vergewissern, dass Sperren wichtig ist, wenn Sie sich jemals denken, dass "diese Operation so atomar ist, dass ich sie nicht sperren muss". – paddy

+0

Schließlich finde ich den Grund, warum es hier sicher ist. Danke allen! [link] (http://www.freertos.org/FreeRTOS_Support_Forum_Archive/February_2010/freertos_portBASE_TYPE_amp_critical_section_3560405.html) – user1603164

Antwort

7

Nein, die Erhöhung der Werte in C ist nicht garantiert atomar. Sie müssen eine Synchronisierung bereitstellen oder eine systemspezifische Bibliothek zum Ausführen atomarer Inkremente/Dekremente verwenden.

+0

Also meinst du es kann ein Bug sein? Auch in einer Single-Core-Umgebung? – user1603164

+1

@ user1603164 * Wenn * der Kontext im "kritischen Bereich" verhindert werden kann. –

+1

@ user1603164 Absolut: Wenn Ihr Code zwischen Laden und Speichern des Wertes liegt, kann der andere Thread den Wert unter Ihnen ändern, was Fehler finden. Dies würde sogar in Single-CPU-, Single-Core- und Nicht-Hyperthread-Umgebungen passieren. – dasblinkenlight

6

Es ist nicht atomar, wie Sie dokumentiert haben. Aber es könnte immer noch "threadsicher" in einem weniger strengen Sinne sein: Ein long kann nicht in einem inkonsistenten Zustand sein. Das Ausmaß der Gefahr hierbei ist, dass, wenn n Fäden vTaskSuspendAll rufen dann uxSchedulerSuspended von irgendwo zwischen 1 und n erhöht werden.

Dies könnte sich aber völlig in Ordnung sein, wenn die Variable ist etwas, das nicht perfekt sein muss, wie ein Tracker, wie oft der Benutzer zu suspendieren gefragt. Es gibt "thread safe" was bedeutet "diese Operation erzeugt das gleiche Ergebnis, egal wie ihre Aufrufe verschachtelt sind" und es gibt "thread safe" was bedeutet "nichts explodiert, wenn Sie dieses aus mehreren Threads aufrufen".

+0

+1 für die vollständigste Antwort. Ich denke, der Kommentar im ursprünglichen FreeRTOS-Code ist verwirrend. Ich wette, dass alles, was FreeRTOS benötigt, ist, dass uxSchedulerSuspended nicht Null zu stoppen ist, so dass es egal ist, ob es 1 oder n inkrementiert wird. –

5

Der Vorgang ist nicht atomar, aber es nirgendwo sagt es ist. Der Code ist zwar threadsicher, aber Sie müssen sehr gut mit dem Code vertraut sein und wissen, wie er in das Design des Schedulers eingepasst wurde. Es spielt keine Rolle, ob andere Tasks die Variable zwischen Laden und Speichern ändern, denn wenn der ausführende Task als nächstes ausgeführt wird, findet er die Variable in demselben Zustand wie beim ursprünglichen Laden (also sind die Modifikations- und Schreibabschnitte immer noch konsistent und gültig)).

Als vorherige geschrieben Notizen kann die lange nicht in einem inkonsistenten Zustand sein, weil es der Basistyp der Architektur ist, auf dem es läuft. Bedenken Sie jedoch, was passiert, wenn der Code auf einer 8-Bit-Maschine (oder 16 Bit) läuft und die Variable 32 Bit hat. Dann wäre es nicht threadsicher, weil die vollen 32 Bits gleichzeitig Byte oder Wort geändert würden und nicht alle auf einmal. In diesem Szenario könnte ein Byte in ein Register geladen, modifiziert und dann in den RAM zurückgeschrieben werden (wobei die anderen drei Bytes unverändert gelassen werden), wenn ein Kontextwechsel auftritt. Wenn die nächste auszuführende Task dieselbe Variable liest, würde sie ein Byte lesen, das geändert wurde, und drei Bytes, die nicht geändert wurden - und Sie haben ein großes Problem.

Grüße, Richard (http://www.FreeRTOS.org)

+0

+1 Wenn die Variable bei der nächsten Ausführung der Task garantiert in demselben Zustand ist, warum sollte diese Annahme nicht gelten, wenn ein Multibyte-Typ inkrementiert wird? – dasblinkenlight