Das deutsche QBasic- und FreeBASIC-Forum Foren-Übersicht Das deutsche QBasic- und FreeBASIC-Forum
Für euch erreichbar unter qb-forum.de, fb-forum.de und freebasic-forum.de!
 
FAQFAQ   SuchenSuchen   MitgliederlisteMitgliederliste   BenutzergruppenBenutzergruppen  RegistrierenRegistrieren
ProfilProfil   Einloggen, um private Nachrichten zu lesenEinloggen, um private Nachrichten zu lesen   LoginLogin
Zur Begleitseite des Forums / Chat / Impressum
Aktueller Forenpartner:

Threadfrage:

 
Neues Thema eröffnen   Neue Antwort erstellen    Das deutsche QBasic- und FreeBASIC-Forum Foren-Übersicht -> Allgemeine Fragen zu FreeBASIC.
Vorheriges Thema anzeigen :: Nächstes Thema anzeigen  
Autor Nachricht
OneCypher



Anmeldungsdatum: 23.09.2007
Beiträge: 802

BeitragVerfasst am: 12.08.2009, 10:21    Titel: Threadfrage: Antworten mit Zitat

In der Befehlsreferenz zu ThreadCreate ( http://www.freebasic-portal.de/index.php?s=reftopic&id=273 ) wird ein Beispiel zum Befehl aufgelistet.

Dort sind mir einige ... ungereimtheiten aufgefallen.

1.
Code:

Sub MyThread ()
  Do 'alle 100ms einen "*" ausgeben
  [...]
  Loop Until terminate=1 'abbrechen wenn 1
End Sub


Dabei wird bei jedem Schleifendurchlauf die globale variable "terminate" auf ihren zustand abgefragt.
Nach meinem Wissen, muss aber jede Variable, welche nicht nur von einen Thread geschrieben/gelesen wird durch einen Mutex geschützt werden.
Es könnte also rein theoretisch zu einem Konflikt kommen, wenn der Thread "terminate" abrufen will und gleichzeitig im Hauptprogramm "terminate" auf 1 gesetzt wird.

2.
Im selben abschnitt wird die Print-anweisung im Thread aufgerufen:
Code:

Sub MyThread ()
    [...]
    Print "*";
    [...]
  Loop [...]
End Sub

Dies geschieht wieder ohne jeden Mutex. Nach meinem verständnis könnte es auch hier zu konflikten mit dem Hauptprogramm kommen, wenn zufällig Hauptprogramm als auch Thread gleichzeitig auf die Standard-ausgabe schreiben wollen.

Irre ich mich da in einer grundsätzlichkeit? Oder ist das jetzt nur zufall das dieses beispiel ohne abstürze läuft?



Nach meinem Verständnis müsste ein korrektes Beispiel in etwa so aussehen:
Code:

Dim As Any Ptr i
Dim Shared As Integer DotCount, terminate
dim shared as any ptr MyMutex

Sub MyThread ()
  dim term_tmp as integer
  Do 'alle 100ms einen "*" ausgeben
    MutexLock MyMutex
    Print "*";
    term_tmp = terminate
    Mutexunlock MyMutex
    Sleep 100, 1
  Loop Until term_tmp=1 'abbrechen wenn 1
End Sub

Print "Hauptprogramm gibt Punkte aus"
Print "Thread gibt Sterne aus"
Print
terminate = 0
i = ThreadCreate(Cast(Any Ptr,@MyThread))

Do  'alle 50ms einen "." ausgeben
  mutexlock MyMutex
  Print ".";
  mutexunlock MyMutex
  Sleep 50, 1
  DotCount += 1
Loop Until DotCount = 100

Mutexlock MyMutex
 terminate = 1 'MyThread abbrechen
 Print
 Print "Beendevorgang begonnen"
Mutexunlock MyMutex
ThreadWait (i) 'warten bis MyThread endet
Print "Thread beendet"
Sleep


Wobei ich auch in dieser Version des Beispiels nicht 100% korrekt bin. Dort habe ich einen einzigen Mutex definiert, der sowohl schreib-vorgänge auf der Standard-Ausgabe als auch die terminate-variable schützt. Das sollte man aus performance-gründen eigentlich voneinander trennen. Aber der Übersichtlichkeit hat man damit nichts abgrungen..
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden
MisterD



Anmeldungsdatum: 10.09.2004
Beiträge: 3071
Wohnort: bei Darmstadt

BeitragVerfasst am: 12.08.2009, 13:49    Titel: Antworten mit Zitat

Variablen lesen ist egal, nur beim Schreiben gibts Probleme, wenn mehrere Schreibzugriffe gleichzeitig stattfinden, oder wenn ein Programm den Wert grade liest um ihn irgendwie weiterzuverarbeiten und direkt danach ein anderer Thread ihn verändert so dass die berechnung des ersten threads danach nicht mehr stimmt. In solchen fällen musst du nen Mutex verwenden.

Solang du aber nur if(wert!=0)EXIT SUB da stehen hast oder sowas brauchst du auch keinen Mutex. Das Schlimmste was dir dabei passieren kann ist, dass halt einer der Threads dann einmal mehr die gesamte Schleife durchläuft, was er sowieso hätte tun können hätte der Scheduler anders gearbeitet, dadurch entstehen dir keine Ungültigen Berechnungsergebnisse oder ähnliches. Und der Schreibzugriff wert=1 vom Thread, welcher das Signal zum Beenden setzt ist ein fester Wert, der kann auch nicht falsch werden wenn irgendein Thread den wert gleichzeitig liest. Daher brauchst du an dieser stelle keinen Mutex.

Und Print ist soweit ich mich erinnere intern schon mit einem Mutex versehen, da jeder normale Print-Befehl auf ein und die selbe Ressource (die Konsole) zugreift. Print war meine ich thread-safe (so bezeichnet man Befehle, bei denen man sich nicht weiter ums Mutexen kümmern muss).

iA solltest du bei Mutexes immer genau überlegen: DENKST du nur du müsstest hier mutexen oder bist du SICHER, dass hier ein Mutex hin muss - denn viele Mutexes machen dein Programm verdammt langsam, die dinger haben nen nicht soo knappen Overhead und setzen außerdem kurzzeitig alle anderen Threads aus (denn auch bei einer Multicore CPU müssen Mutexes zwischen allen Kernen gleichzeitig ausgewertet werden, ansonsten könnten eben zwei Kerne gleichzeitig denken "grade nicht gelockt", dann beide "dann lock ich jetzt" und schon hast du zwei threads die den Mutex parallel gelockt haben -> kaputt). Also Mutex nur einsetzen, wo es wirklich nötig ist, und nicht einfach auf gut Glück ohne viel drüber nachzudenken.
_________________
"It is practically impossible to teach good programming to students that have had a prior exposure to BASIC: as potential programmers they are mentally mutilated beyond hope of regeneration."
Edsger W. Dijkstra
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden
ThePuppetMaster



Anmeldungsdatum: 18.02.2007
Beiträge: 1839
Wohnort: [JN58JR]

BeitragVerfasst am: 12.08.2009, 14:21    Titel: Antworten mit Zitat

@MisterD .. Sowohl lesende als auch schreibede zugriffe auf speicher müssen thread-seitig geschützt werden. Auch beim Lesen von Daten kann es zu konflikten kommen.

"Print" ist Threadsafe, wenn die kompileroption von FBC beim kompilieren verwendung findet.


MfG
TPM
_________________
[ WebFBC ][ OPS ][ ToOFlo ][ Wiemann.TV ]
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden
volta



Anmeldungsdatum: 04.05.2005
Beiträge: 1876
Wohnort: D59192

BeitragVerfasst am: 12.08.2009, 15:07    Titel: Antworten mit Zitat

Hi,
ich sehe das so wie MisterD und kann mir im Moment keinen kritischen Zustand beim lesen von Variablen vorstellen.
Da hier nur von einer Stelle (Thread) die Variable geändert wird sehe ich auch kein "muß" für ein Mutex.
Zitat:
"Print" ist Threadsafe, wenn die Kompileroption von FBC beim kompilieren Verwendung findet.
das, glaube ich, hab ich auch im engl. Forum so gelesen.
_________________
Warnung an Choleriker:
Dieser Beitrag kann Spuren von Ironie & Sarkasmus enthalten.
Zu Risiken & Nebenwirkungen fragen Sie Ihren Therapeuten oder Psychiater.


Zuletzt bearbeitet von volta am 12.08.2009, 15:27, insgesamt einmal bearbeitet
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden Website dieses Benutzers besuchen
MisterD



Anmeldungsdatum: 10.09.2004
Beiträge: 3071
Wohnort: bei Darmstadt

BeitragVerfasst am: 12.08.2009, 15:25    Titel: Antworten mit Zitat

okay TPM, dann erklär mir mal was beim simultanen lesen in zwei threads schief gehen kann? Oo
_________________
"It is practically impossible to teach good programming to students that have had a prior exposure to BASIC: as potential programmers they are mentally mutilated beyond hope of regeneration."
Edsger W. Dijkstra
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden
OneCypher



Anmeldungsdatum: 23.09.2007
Beiträge: 802

BeitragVerfasst am: 12.08.2009, 15:27    Titel: Antworten mit Zitat

@Volta & MisterD:

Man stelle sich vor, die variable die vom Thread gelesen werden soll, ist ein Zeiger. Das kann ja vorkommen.

Und nun ändert das Hauptprogramm den Zeiger während ein Thread genau auf den zeiger drauf zugreift. Was passiert dann im detail? Gibt es eine art "freigabe" status für bytes im Hauptspeicher? Ab wann ist ein byte vollständig geschrieben?
Nun begeb ich mich mal auf glattes eis: Ich stelle mir den Bytes im Hauptspeicher wie Schieberegister vor, wo von links oder von rechts, das neue byte "reingeschoben" wird. Pro Takt erhält das Byte stück für stück seinen neuen wert. Also spätestens nach 8 taktflanken, sollte das Byte im Speicher stehen.
Wenn nun aber prozesse "asynchron" auf so ein byte zugreifen, kann der eine prozess vom anderen nicht wissen wie weit er fertig ist mit "schieben".
Dann könnte es während des "schiebens" vorkommen das total unrealistische werte im byte stehen. Im schlimmsten fall nimmt der thread einen falschen zeiger entgegen und will an der stelle wo der zeiger hinzeigt, was wegschreiben oder gar code ausführen.. so wäre ein Absturz praktisch "vorprogrammiert".

Oder sind diese ... pthread-bibliotheken so intelligent das sie sowas erkennen?
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden
OneCypher



Anmeldungsdatum: 23.09.2007
Beiträge: 802

BeitragVerfasst am: 12.08.2009, 15:32    Titel: Antworten mit Zitat

PS: Was ist "Threadsafe"? .. welcher Basic-Anweisung kann ich es ansehen das sie "threadsafe" ist? Wie schauts mit PSET, DrawString, PUT etc aus? sind die "Threadsafe"?
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden
volta



Anmeldungsdatum: 04.05.2005
Beiträge: 1876
Wohnort: D59192

BeitragVerfasst am: 12.08.2009, 15:51    Titel: Antworten mit Zitat

Ok @OneCypher,
was du da als Vision hinstellst ist schon ein Programmierfehler und nicht ein einfacher Zugriff auf einen Speicherwert. Wenn zwei Kerne auf den Datenbereich zugreifen, wird einer den Zugriff und der Andere "Wait-Zyklen" erhalten (keine kritische Situation).

Nein, alles was auf den screen-Speicher zugreift ist nicht "thread-safe".
Man kann keiner Basicanweisung ansehen ob sie "thread-safe" ist, muss man von Fall zu Fall untersuchen.


Edit/
diese Antwort war nicht an TPM gerichtet verlegen
_________________
Warnung an Choleriker:
Dieser Beitrag kann Spuren von Ironie & Sarkasmus enthalten.
Zu Risiken & Nebenwirkungen fragen Sie Ihren Therapeuten oder Psychiater.


Zuletzt bearbeitet von volta am 12.08.2009, 22:15, insgesamt 2-mal bearbeitet
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden Website dieses Benutzers besuchen
MisterD



Anmeldungsdatum: 10.09.2004
Beiträge: 3071
Wohnort: bei Darmstadt

BeitragVerfasst am: 12.08.2009, 15:59    Titel: Antworten mit Zitat

Ein Speicherregister ist entweder geschrieben oder nicht, jede CPU arbeitet getaktet. du kannst keine halb geschriebenen Register lesen, abgesehen davon dass der eine Kern eh erstmal in seinen cache schreibt und nicht direkt in den echten Ram, und der andere Kern aus einem völlig anderen cache liest weil jeder Kern eigene Caches hat, die zwei threads arbeiten also auf verschiedenen kopien des gleichen Speicherbereichs, nichteinmal auf dem selben physischen Speicherplatz.
_________________
"It is practically impossible to teach good programming to students that have had a prior exposure to BASIC: as potential programmers they are mentally mutilated beyond hope of regeneration."
Edsger W. Dijkstra
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden
volta



Anmeldungsdatum: 04.05.2005
Beiträge: 1876
Wohnort: D59192

BeitragVerfasst am: 12.08.2009, 16:20    Titel: Antworten mit Zitat

Wobei @MisterD,
der Kern, der einen Thread abarbeitet liest nur die "internen" Daten in den Cache. "Externe" Daten müssen aus dem RAM (am Cache vorbei) gelesen werden sonst würde er keine Änderung bemerken.
_________________
Warnung an Choleriker:
Dieser Beitrag kann Spuren von Ironie & Sarkasmus enthalten.
Zu Risiken & Nebenwirkungen fragen Sie Ihren Therapeuten oder Psychiater.
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden Website dieses Benutzers besuchen
OneCypher



Anmeldungsdatum: 23.09.2007
Beiträge: 802

BeitragVerfasst am: 12.08.2009, 16:45    Titel: Antworten mit Zitat

d.h. wenn ich so programmiere, dann kann es im schlimmsten fall passieren, das in wahrheit 2 mutexe bestimmte bereiche schützen? Wie im fall mit der anweisung "Print" ?.. kann ja keiner hellsehen das da intern ein mutex vor parallelen zugriffen schützt..
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden
volta



Anmeldungsdatum: 04.05.2005
Beiträge: 1876
Wohnort: D59192

BeitragVerfasst am: 12.08.2009, 17:42    Titel: Antworten mit Zitat

Na und zwinkern
besser doppelt gemoppelt als garnicht!

Aber ist alles halb so schlimm..
http://de.wikipedia.org/wiki/Threadsicherheit hat Folgendes geschrieben:
.. Elementarfunktionen von Programmiersprachen, die nur lokale Variablen bearbeiten, sind immer threadsicher, so zum Beispiel die meisten mathematischen Funktionen, die Zwischenergebnisse immer auf dem Stack ablegen und immer auf Kopien der Originalvariablen arbeiten.

_________________
Warnung an Choleriker:
Dieser Beitrag kann Spuren von Ironie & Sarkasmus enthalten.
Zu Risiken & Nebenwirkungen fragen Sie Ihren Therapeuten oder Psychiater.
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden Website dieses Benutzers besuchen
ThePuppetMaster



Anmeldungsdatum: 18.02.2007
Beiträge: 1839
Wohnort: [JN58JR]

BeitragVerfasst am: 12.08.2009, 18:11    Titel: Antworten mit Zitat

@volta vision? .. ich hab doch auf deine Frage noch garnicht geantwortet grinsen

Lokale Variablen sind, wie du schon geschrieben hast, immer Threadsafe.

Lesende Aktionen können Problematisch werden, wenn die Datenmängen grösserer Natur sind, als das sie mit einem Cyklus geschrieben / gelesen werden können.

2 Lesende Threads sind in der tat Threadsafe. Kommt jedoch auch eine Schreibene (Worauf es bei Variablen ja schliesslich ankommt) hinzu kommt, können bei lesenden Aktionen die Daten verfälscht werden.

Liest man z.B. 1K Daten ein (grösseres UDT), wird das nicht mit einem Zyklus eingelesen, sondern in mehreren Schritten. Wird beim einlesen der Thread gewechselt, und eine Schreibaktion ausgeführt, hat der Thread die nötige Zeit das durchzuführen. Dabei überschreibt jedoch der 2te Thread die UDT Daten und der alte Thread wird beim nächsten Wechsel mit nicht mehr koherenten Daten weiter arbeiten, bzw. den Rest einlesen.

Ein einfaches Problembeispiel ist z.B. Strings in UDT's. Sie sind 3 Byte gross, und können daher abgeschnitten werden, wenn es zum einlesen kommt. Ich rede hier vom String-Header, nicht von den eigentlichen Daten.
Im Header ist hinterlegt, wie gross die daten sind, bzw. wie gross der allokierte Speicher.
Würde wärend dem Lesen z.B. dieser verändert, und der erste Thread hat erst das erste Byte des Headers eingelesen, würden diskontinuitäten auftretten, welche durchaus Problematisch sein können.

Ein Anderes Beispiel: Ein Thread soll ein String ausgeben. Er liest den Header, um den Alloc-Pointer zu erhalten. Ein anderer Thread ändert den Pointer (Textvergrösserung). Thread 1 würde jetzt mit einem Falschen Pointer arbeiten. Dies würde zu einem MAE führen.


MfG
TPM
_________________
[ WebFBC ][ OPS ][ ToOFlo ][ Wiemann.TV ]
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden
volta



Anmeldungsdatum: 04.05.2005
Beiträge: 1876
Wohnort: D59192

BeitragVerfasst am: 12.08.2009, 22:30    Titel: Antworten mit Zitat

ja, mit den Augen rollen verlegen peinlich äähh vertan, vertan

Hi TPM,
ok, du zählst da Beispiele auf, die ich dann auch mit Mutex ausführen würde, da gebe ich dir recht.
Es bleibt aber dabei, für einen einfachen Lesezugriff auf eine Variable, wie im Anfangsbeispiel, ist mutexen kein "muß".

(letzte Beispiel:
Zitat:
Ein Thread soll ein String ausgeben. Er liest den Header, um den Alloc-Pointer zu erhalten. Ein anderer Thread ändert den Pointer (Textvergrösserung). Thread 1 würde jetzt mit einem Falschen Pointer arbeiten. Dies würde zu einem MAE führen.
No, überleg mal warum nicht? grinsen
Code:
Dim As String Ptr aptr, bptr
Dim As String a = "alter Text", b = " erweitert"

aptr = @a
Print a, aptr

a = a + b
bptr = @a
Print a, bptr

Print *aptr, aptr
Sleep
)
_________________
Warnung an Choleriker:
Dieser Beitrag kann Spuren von Ironie & Sarkasmus enthalten.
Zu Risiken & Nebenwirkungen fragen Sie Ihren Therapeuten oder Psychiater.
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden Website dieses Benutzers besuchen
ThePuppetMaster



Anmeldungsdatum: 18.02.2007
Beiträge: 1839
Wohnort: [JN58JR]

BeitragVerfasst am: 13.08.2009, 07:32    Titel: Antworten mit Zitat

Doch doch ... meiner ansicht nach, kann das problematisch werden.

Beispiel:

UDT
Code:

type bla
    X as String ' 12 Byte
    Y as String ' 12 Byte
end type


UDT Speicher
[0 1 2 3 4 5 6 7 8 9 10 1112 13 14 15 16 17 18 19 20 21 22 23]

Thread 1 kopiert UDT lokal zwischen
Code:

dim a as bla
dim b as bla
a = b


gehen wir von einem 4 Byte kopiervorgang aus.
Kopiervorgang 1: [0 1 2 3]
Kopiervorgang 2: [4 5 6 7]
Kopiervorgang 3: [8 9 10 11]
Kopiervorgang 4: [12 13 14 15]
Kopiervorgang 5: [16 17 18 19]
Kopiervorgang 6: [20 21 22 23]

Wechselt der Thread in einem der Kopiervorgänge, wird z.B. nur der Kopiervorgang 1 und 2 ausgeführt.
Der neue Thread schreibt nun (da ausreichend Zeit vorhanden ist) neue Daten in alle 2 Strings

Nach dem erneutem wechsel zum alten Thread wird der kopiervorgang vorgeführt und abgeschlossen.

Nach dem Kopiervorgang stehen im header die Informationen zum Allokiertem Speicher, der Datengrösse und dem Pointer zum eigentlichem Datenbereich.
Durch die abänderung von Thread-2 kann sich bei veränderung der Daten der Pointer auf den allokierten bereich Ändern.
Da jedoch beim kopiervorgang des Pointers dieser verändert wurde, kann der thread nicht merh auf den richtigen Datenbereich zugreifen, und folglich auch nicht mehr den String kopieren, der durch den Pointer angegeben wird. Und hier ist das Problem, meiner ansicht nach.

Die Zusammensetzung des UDT's ist hier, meiner ansicht nach entscheident, wenn dieses Kopiert wird. Vorallem bei String's, da diese nicht in einem Kopiervorgang übertragen werden können, sondern in mehrere Zyklen aufgeteilt werden.


MfG
TPM
_________________
[ WebFBC ][ OPS ][ ToOFlo ][ Wiemann.TV ]
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden
MisterD



Anmeldungsdatum: 10.09.2004
Beiträge: 3071
Wohnort: bei Darmstadt

BeitragVerfasst am: 13.08.2009, 09:17    Titel: Antworten mit Zitat

bei den Strings wär ich mir garnicht mal so sicher, da FB glaub ich für jede Operation eh ne neue Kopie anlegt, d.h. die halb-kopierte originalversion bleibt ungeändert im speicher wenn ein anderer thread sie manipuliert, weil der sie auch nur liest und kopiert..
_________________
"It is practically impossible to teach good programming to students that have had a prior exposure to BASIC: as potential programmers they are mentally mutilated beyond hope of regeneration."
Edsger W. Dijkstra
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden
ThePuppetMaster



Anmeldungsdatum: 18.02.2007
Beiträge: 1839
Wohnort: [JN58JR]

BeitragVerfasst am: 13.08.2009, 11:37    Titel: Antworten mit Zitat

wie meinst du das? .. das fb ne kopie des originals macht, und dann erst die kopie kopiert? ... ?!?!

also .. nach meinem verständniss des RTLIB-Codes, wird das einfach 1 zu 1 kopiert .. ohne zusätzlice kopie ..

oder wie meinst du das?


MfG
TPM
_________________
[ WebFBC ][ OPS ][ ToOFlo ][ Wiemann.TV ]
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden
MisterD



Anmeldungsdatum: 10.09.2004
Beiträge: 3071
Wohnort: bei Darmstadt

BeitragVerfasst am: 13.08.2009, 14:00    Titel: Antworten mit Zitat

nein ich meine, dass während ein thread den string "manipuliert" und ein anderer das selbe tut beide lediglich kopien erstellen und das original aber unangetastet lassen, in dem fall könnten sie beide gleichzeitig drauf arbeiten.. aber nur so n gedanke, kA ob das wirklich geht oder sonst was
_________________
"It is practically impossible to teach good programming to students that have had a prior exposure to BASIC: as potential programmers they are mentally mutilated beyond hope of regeneration."
Edsger W. Dijkstra
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden
Beiträge der letzten Zeit anzeigen:   
Neues Thema eröffnen   Neue Antwort erstellen    Das deutsche QBasic- und FreeBASIC-Forum Foren-Übersicht -> Allgemeine Fragen zu FreeBASIC. Alle Zeiten sind GMT + 1 Stunde
Seite 1 von 1

 
Gehe zu:  
Du kannst keine Beiträge in dieses Forum schreiben.
Du kannst auf Beiträge in diesem Forum nicht antworten.
Du kannst deine Beiträge in diesem Forum nicht bearbeiten.
Du kannst deine Beiträge in diesem Forum nicht löschen.
Du kannst an Umfragen in diesem Forum nicht mitmachen.

 Impressum :: Datenschutz