2015-09-19 12 views
6

Ich habe ein C++ - Programm, das einige globale Variablen deklariert. Danach teilt es sich in mehrere Threads auf, um mehrere Aufgaben zu erledigen. Diese Threads lesen und schreiben einige dieser globalen Variablen.C++ Multithreading mit globalen Variablen verstehen

Wird es eine App-Absturz, wenn zwei Threads Lesen die gleiche Variable sind? Oder wird es nur dann einen App-Crash geben, wenn ein Thread an eine Variable schreibt, die ein anderer Thread gerade liest?

Wenn also die Antwort auf meine zweite Frage ja wäre, würde das folgende Codebeispiel dieses Problem lösen?

#include <string> 
#include <thread> 
#include <mutex> 
using namespace std; 

mutex m; 
string var = "foo"; 

// function to provide read and write access 
// "protected" with mutex 
string test(string value = "") 
{ 
    m.lock(); 
    if (value == "") 
    { 
     m.unlock(); 
     return var; 
    } 
    else 
    { 
     var = value; 
     m.unlock(); 
     return ""; 
    } 
} 

void thread1() 
{ 
    // use global variable local 
    string localVar = test(); 
} 
void thread2() 
{ 
    // overwrite global variable 
    test("bar"); 
} 
void thread3() 
{ 
    // use global variable local 
    string localVar = test(); 
} 

int main() 
{  
    thread t1(thread1); 
    thread t2(thread2); 
    thread t3(thread3); 

    t1.join(); 
    t2.join(); 
    t3.join(); 

    return 0; 
} 

weiterhin gilt: ist dieser Teil

// ... 
if (value == "") 
{ 
    m.unlock(); 
    return var; 
} 
// ... 

auch thread retten?

Und meine letzte Frage: mein Programm verwendet derzeit nur einen Mutex, um zu verhindern, dass zwei Threads (die gleiche Funktion!) Gleichzeitig ausgeführt werden. Ich verwende keine Mutexe für meine globalen Variablen. Könnte es sein, dass diese "Situation" einen App-Absturz (Modul: "ntdll.dll") mit dem Ausnahmecode 0xc0000005 verursachen kann?

Vielen Dank im Voraus!

+1

BTW, gebrauchte _scoped Schlösser_ bitte! –

Antwort

6

Multiple Reads sind immer Thread-sicher. Sobald ein Thread in eine nicht-atomare Variable var schreibt, während andere Threads von var lesen, besteht die Gefahr einer Wettlaufsituation. Sie sind also fast da, aber die Verwendung Mutex Wachen (sie sind RAII und daher Ausnahme sicher und sauberere C++), so etwas wie:

#include <mutex> 
#include <string> 
// ... 
std::mutex m; 
std::string var = "foo"; 
// ... 
std::string test(const std::string& value = "") 
{ 
    std::lock_guard<std::mutex> lock(m); 
    if (value == "") 
    { 
     return var; 
    } 
    else 
    { 
     var = value; 
     return ""; 
    } 
} 
+0

danke für deine Antwort! Aber was macht 'lock_guard (m)' "besser" als 'm.lock()'? Was ist der Unterschied? – SaschaP

+0

Nichts, was Sie einfach nicht schreiben müssen, um zu entsperren. – Logman

+0

@SaschaP - Wenn eine Ausnahme ausgelöst wird, wird der Mutex entsperrt –

0

Einfache Frage, einfache Antwort:

Wird ein App-Crash da sein, wenn zwei Threads die gleiche Variable lesen?

Nr

Wird nur eine App-Crash da sein, wenn ein Thread auf eine Variable schreibt, die ein anderer Thread derzeit liest?

Nr

aber Sie sollten wirklich Schlösser und mutexes usw. verwenden, um sicherzustellen, dass Sie die erwartete Ausgabe für Programme erhalten. Obwohl das Programm selbst nicht abstürzt, wenn ein Thread in eine Variable schreibt, die von einem anderen Thread gelesen wird, welchen Wert soll der Lese-Thread tatsächlich lesen? Der Wert vor dem Überschreiben oder der Wert danach?

+0

Ich überwache eine serielle Schnittstelle und wenn neue Daten verfügbar sind überschreibe ich die globale Variable. Der zweite Thread schreibt diese Variable (und andere) alle x Sekunden in eine Datei. Also möchte ich, dass mein Lese-Thread die Variable empfängt, nachdem sie aktualisiert wurde ... – SaschaP

+0

Dann sperren Sie diese Variablen am besten !! Verwenden Sie lock_guard , so dass Sie eine Entsperrung nicht manuell schreiben müssen, wenn der Sperrschutz außerhalb des Gültigkeitsbereichs am Ende des Codeblocks den Mutex freigibt –

+0

Wenn Sie mit serieller Schnittstelle und Threads arbeiten, benötigen Sie wahrscheinlich etwas wie Pufferklasse mit Schutzzugang (threadsicher) dazu. Die Verwendung einer reinen Zeichenfolge als Puffer kann zu seltsamen Verhalten führen und zum Absturz führen. – Logman

0

nicht Thread-sicher ohne mutexes

vorgeschlagene Lösung ist noch nicht ganz richtig.

var wird außerhalb des Mutex gelesen und könnte zu diesem Zeitpunkt geändert werden.

Das sieht wie C++ 11 aus. Wenn std::string Shared Strings verwendet (in C++ 11 gesperrt), könnte das Probleme beim Lesen verursachen.

Beim Lesen + Schreiben konnte c0000005 (Zugriffsverletzung) auftreten, wenn ein Zeiger beim Kopieren geändert wurde.

5

Wird es eine App-Absturz, wenn zwei Threads sind die gleiche Variable zu lesen?

Nein. Niemals. Wenn Sie nur aus mehreren Threads lesen, sind Sie immer sicher.

wird es eine App-Crash nur sein, wenn ein Thread auf eine Variable schreibt, die ein anderer Thread lesen ist zur Zeit?

Nicht genau, aber es kann führen zu einem Absturz, und das ist, was in Ihrem Code geschieht. Es ist nicht "gefährlich" in Bezug auf den Absturz einer Anwendung zum Lesen/Schreiben von mehreren Threads gleichzeitig. Der schlimmste Fall ist, dass Sie an einigen Stellen Werte erhalten. Das wird deine App nicht zum Absturz bringen, aber es kann definitiv dazu führen. Das Problem liegt vor, wenn die Daten, die Sie lesen, eine andere Bedeutung als ein primitiver Wert haben (zB eine ganze Zahl). Wenn Sie beispielsweise eine Speicheradresse (Zeiger) lesen und dann versuchen, auf den Speicher unter dieser Adresse zuzugreifen, aber der Speicher ist bereits freigegeben, sind Sie in Schwierigkeiten - und genau das passiert in Ihrem Code. Die Zeichen der Zeichenfolge sind an eine neue Adresse verschoben worden, Sie versuchen jedoch, die alte Adresse zu lesen.

Ihr Problem zu lösen, sollten Sie den gesamten Betrieb innerhalb des Schloss wickeln, und man konnte eine temporäre Variable für diesen Einsatz:

string test(string value = "") 
{ 
    m.lock(); 
    if (value == "") 
    { 
     string temp = var; 
     m.unlock(); 
     return temp; 
    } 
    else 
    { 
     var = value; 
     m.unlock(); 
     return ""; 
    } 
} 

Die richtige Lösung ist in Paulus 'Antwort .

+0

danke für deine Antwort! +1 für die verständliche Antwort und die Lösung mit der temporären Variable ;-) – SaschaP

0

Wird es eine App-Absturz, wenn zwei Threads sind Lesen das gleiche Variable?

Nein, Sie können die globale Variable gleichzeitig sicher lesen (wenn Sie wissen, dass niemand zur selben Zeit schreibt). Die Leseoperation ändert den globalen Wert nicht, so dass er gleich bleibt und alle Leser den gleichen Wert "sehen".

Oder wird ein App-Crash dort nur dann, wenn ein Thread auf eine Variable schreibt, die ein anderer Thread zu lesen ist zur Zeit?

Im Allgemeinen nicht, zumindest nicht wegen der gleichzeitigen Lese-Schreib-Operation. Der Absturz kann eine Nebenwirkung davon sein. Wenn Sie beispielsweise einen Zeigerwert aktualisieren und gleichzeitig lesen, versuchen Sie, auf die Daten zuzugreifen, auf die der Zeiger zeigt. Wenn der gelesene Wert ungültig ist, werden Sie wahrscheinlich abstürzen.

weiterhin gilt: ist dieser Teil

// ... 
if (value == "") 
{ 
    m.unlock(); 
    return var; 
} 
// ... 

auch thread retten?

Nr Ihre Mutex m nur die lokale Variable value schützt, die keine Notwendigkeit, geschützt werden muss, da es lokal ist. Aber dann geben Sie den Mutex frei und kopieren (lesen) die globale Variable var, während ein anderer Thread darauf schreiben kann. Um es threadsicher zu machen, benutzen Sie entweder std::lock_guard und dann müssen Sie den Mutex nicht manuell sperren/entsperren. oder aktualisieren Sie den Code dazu:

m.lock(); 
if (value == "") 
{ 
    string ret_val(var); 
    m.unlock(); 
    return ret_val; 
} 

Ich bin nicht mutexes für meine globalen Variablen. Könnte es sein, dass diese „Situation“

ein app-Absturz führen kann Wie ich bereits schrieb, ja, als Nebeneffekt die App zum Absturz bringen kann.

+0

Wer war so eifersüchtig und unten abgestimmt ohne zu erklären warum? –

+0

war es nicht ich! – SaschaP

+0

@SaschaP Ich habe dich nicht beschuldigt :) Jeder kann hier abstimmen, aber ich würde gerne eine Erklärung zur Abstimmung sehen, damit ich daraus lernen kann. Wie auch immer, es wurde zurückgewählt. –