C++ Daten konvertieren

Nein, ich will keine "Daten" übergeben, ich will eine einzelne Zahl übergeben und sonst nichts.
Eine Zahl die der Ersteller des Thread hinterher schlicht nicht mehr braucht, dafür will aber der Ersteller des Thread weiter arbeiten und nicht warten bis der Thread mal diesen Wert gelesen hat.
Noch genauer, ich weiss eh schon, wie ich diese Zahl auf einfachst mögliche Weise übergeben kann, darum ist die ganze Diskussion darum ob man das so machen sollte oder nicht ohnehin sinnfrei und darum habe ich in der Eingangsfrage den Sinn und Zweck des Ganzen weggelassen.

Deine Warnungen ala " nicht manchmal crashed oder mit einem anderen/neuen Compiler plötzlich nicht mehr funktioniert" kenne ich nun schon über die Jahre aus 100 anderen Unterhaltungen wo mir dies immer wieder erklärt wird.
Interessanterweise sind es aber die Projekte dieser Leute die immer wieder crashen, während mein Projekt seit über 10 Jahren keinerlei Bug mehr gehabt hat, ich habe das Server-Ende von meinem Gameserver schon über 2 Jahre am Stück laufen gehabt, ohne Neustart, ohne Bug, ohne Crash, schlicht wartungsfrei und das wird mit der neuen Funktion die ich da jetzt gerade einbauen will auch nicht anders werden.

Das grösste Problem was ich jemals hatte war dieses hier vor einem halben Jahr:
https://www.computerbase.de/forum/t...n-update-geaendert-kein-dateizugriff.1938533/
Da hat M$ den Support für einen alten bmp-header eingestellt und das führte dazu, dass meine Client-Software ihre teilweise 20 Jahre alten Grafiken nicht mehr laden konnte, aber das sind halt Probleme für die auch der beste standard-konforme Code nichts kann.

Es dauert halt immer eine Weile, bis ich durch die ganzen Off-Topic-Antworten durch bin, wo mir irgendwer sinngemäss erklären will, dass ich ein Trottel bin der alles ganz grundsätzlich falsch macht, bevor ich mal die simple Antwort bekomme nach der ich suche - in diesem Falle kam sie von @wayne_757 und ist bis inkl. diesem Beitrag jetzt, immer noch die einzige direkte Antwort auf meine Frage.
Was ihr hier als XY-Frage bezeichnet ist einfach nur mein Versuch mir diese Off-Topic-Antworten zu ersparen, weil die mir ausser dass ich stundenlang Erklärungen tippen darf nichts bringen.
 
Zuletzt bearbeitet:
Was ihr hier als XY-Frage bezeichnet ist einfach nur mein Versuch mir diese Off-Topic-Antworten zu ersparen, weil die mir ausser dass ich stundenlang Erklärungen tippen darf nichts bringen.
Sorry, dass man versucht hat dir wirklich zu helfen, statt nur ein Symptom zu bekämpfen - wird sicher nie wieder vorkommen, oder was willst du auf sowas hören?

Mal ernsthaft: Wenn du bereits gut genug wärst, um genau zu wissen, dass du den richtigen Weg zu deiner Problemlösung gewählt hast, dann hättest du nicht so eine Frage hier im Forum gepostet. Und dann wären die Formulierungen in der Frage technisch "besser" gewesen (bitte nicht angegriffen fühlen).
Hier sind halt Leute mit vielen Jahren/Jahrzehnten C/C++ Erfahrung und die lasses es sich ungern nehmen mal einen Tipp "Über den Tellerrand hinaus" zu geben.

Da deine Frage wirklich sehr umständlich klang, lag die Vermutung nahe, dass es für "das eigentliche Problem" einen besseren Lösungweg gibt als den von dir gewählten. Es passiert hier täglich, dass Leute nicht das einfachste Werkzeug zur Lösung ihres Problems wählen.

Wie gehst du damit um, wenn der Thread mal 2 Parameter braucht? Oder wenn du diesen Teil der SW crossplattform machen musst? Gegen Win32 oder posix programmiert kein Mensch, wenn es sich vermeiden lässt. Der wait ließe sich vermeiden, wenn man die Parameter der Threads nicht nur vorher aufm Stack allokiert sondern persistenter bis nach dem .join() erhält. Sowas braucht auch keinen fehleranfälligen malloc. zB nen class-member tuts auch. Also sowas in der Art:
C++:
class ThreadSpawner
{
  const size_t numOfThreads = 4;
  std::vector<ThreadType> m_threads[numOfThreads];
  std::vector<MyThreadParameter> m_threadParams[numOfThreads];
};
Das ist imho 100x lesbarer und nicht umständlich oder ein riesen Umweg für dich. Da wartet nix und du hast keine potentiellen memoryleaks mit malloc.
Das lässt sich sowohl mit nem Debugger als auch mit printf gut analysieren, weil man von jedem (main/sub)-thread heraus sehen kann, welcher Thread mit welchen Werten arbeitet.

Hier fragen außerdem oft Schüler/Studenten oder Hobby-Programmierer, die lernbereiter und interessierter sind für "nimm doch Powershell statt batch. Mit deinem 10 Seiten batch bist du an der Grenze der Wartbarkeit angekommen. Hier ist schonmal ein Beispiel: ...".

Für so technisch spezifische Fragen wie in deinem Fall ist https://stackoverflow.com/ viel besser, wenn man keine Diskussionen lesen will.
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: BlackMark
kuddlmuddl schrieb:
Sorry, dass man versucht hat dir wirklich zu helfen, statt nur ein Symptom zu bekämpfen - wird sicher nie wieder vorkommen, oder was willst du auf sowas hören?

Na ok, nachdem das Thema an sich erledigt ist und ich gerade nichts Besseres zu tun habe, gebe ich dir gerne eine Antwort, die halt nu sehr weit off Topic geht.

Das Projekt um das es hier geht ist gleichzeitig das einzige Projekt was ich jemals in C/C++ geschrieben habe und es wird auch das einzige Projekt bleiben, ich bin derweil in Rente und habe dies schon vorher nur als Hobby betrieben.
Noch genauer: Ich hatte eigentlich nie vor überhaupt in C/C++ oder sonst irgendeine Programmierung jenseits von HTML/PHP einzusteigen und selbst HTML/PHP nutze ich ausschliesslich zu meinem eigenen Vergnügen, sprich zur Erstellung meiner eigenen Homepage (die genau wie mein Gameserver kostenlos und werbefrei, also reines Hobby ist).

Vorgeschichte:
Ich war vor 25 Jahren mal ein Profi-Backgammonspieler, sprich ich habe mit Backgammon um Geld meinen Lebensunterhalt verdient und war nebenbei auch auf mehreren kostenlosen Seiten unterwegs, wo es um Matchanalysen und so Zeug ging, inkl. diversen Diskussionsforen rund um das Spiel (da habe ich seinerzeit die Frau kennengelernt mit der ich heute verheiratet bin).
Damals gab es mehrere Dutzend Anbieter von Gameservern auf denen man sowohl kostenlos als auch um Geld Backgammon spielen konnte, nur wurden die dann halt alle gierig, sprich die Kommissionen beim Spiel um Geld wurden so lange in die Höhe geschraubt bis die Spieler weggelaufen sind und die kostenlosen Dienste wurden so lange mit Werbung zugemüllt bis es nicht mehr spielbar war.
Abgesehen davon hatte so gut wie überhaupt kein Anbieter eine Ahnung davon, was für die Spieler nützliche Funktionen von Online-Backgammon wären, die haben alle nur auf ihren Profit geschaut und die Qualität der Software für die User war Nebensache.
Völlig logisch gingen dann die Betreiber einer nach dem Anderen Pleite und als dann auch noch die MSN-Gaming-Zone (der seinerzeit weltweit grösste Server dieser Art, zumindest für kostenloses Spielen) geschlossen wurde waren einige 100.000 User weltweit inkl. mir selber sozusagen heimatlos.
Ich hatte dann mit einigen anderen Usern in einer Google-Newsgroup eine Diskussion darüber, wie schwer es denn wohl wäre selber so einen Gameserver zu coden und ich war der Einzige der gesagt hat, sowas könnte ich zur Not im Alleingang in ein paar Monaten coden, womit ich mich recht weit aus dem Fenster gelehnt habe, weil ich zu der Zeit nicht die allergeringste Ahnung von Programmieren jenseits von PHP hatte.
Die Wellen kochten hoch, alle Anderen meinten dazu braucht man ein ganzes Team von Profis und es kostet einen Haufen Geld und weil ich der Einzige an meinem Ende der Diskussion war blieb mir schon nach kurzer Zeit gar nichts Anderes übrig als zu sagen: "OK, gebt mir ein paar Wochen, ich zeige euch, dass das im Alleingang geht."

Wie gesagt, an der Stelle wusste ich überhaupt NICHTS über Programmieren, mal abgesehen von etwas Basiswissen in PHP.
Ich habe also derartig tief von Grund auf angefangen, dass ich mir von diversen Programmier-Webseiten erstmal rausgelesen habe, welche Programmiersprache ich überhaupt verwenden soll und da fiel am Ende die Wahl auf simples C, einfach weil ich dazu die meisten Anfänger-Tutorials gefunden habe, innerhalb weniger Tage auf C++ erweitert, nicht weil ich volles C++ verwenden wollte, sondern weil es ein paar Dinge gab die ich machen wollte, die so wie ich das wollte in reinem C nicht machbar waren.

Es hat mich 6 Wochen gedauert in denen ich ausser essen, schlafen und coden nichts Anderes gemacht habe, bis inkl. überhaupt mal Grundkenntnisse der Programmierung lernen, die allererste, sehr rudimentäre, aber funktionstüchtige Version meiner Software online war und die war mal abgesehen von echt grauenhafter Grafik in der Funktionalität schon benutzerfreundlicher als die meisten professionellen Gameserver.
An der Stelle wäre EIGENTLICH schon das Ende meiner Programmier-Karriere gekommen, denn die Diskussion in der Google-Newsgroup habe ich damit ganz glatt gewonnen und mehr hatte ich gar nicht vor.
Nur fand meine Frau (damals noch Verlobte) meine Software so gut, dass sie das Projekt behalten und mit ihren Freunden nutzen wollte.
Darum schreibe ich seitdem immer wenn ich mal Zeit und Lust dazu habe Updates für die Software, baue neue Funktionen ein, usw., die aber weniger den Code an sich betreffen, sondern Dinge wie bessere Grafik oder Übersetzung in mehrere Sprachen.
Die Anzahl User auf dem Server liegt aber im 1-stelligen Bereich, einfach weil das Projekt nicht nur keine Werbung enthält und darum keine Einnahmen generiert, sondern ich selber auch keinerlei Werbung für das Projekt mache, sprich das Ganze ist nun seit 20 Jahren online, aber die Adresse kennt kaum wer, mal abgesehen davon, dass Online-Backgammon leicht aus der Mode gekommen ist.
Kein Mensch würde auch nur im Ansatz darüber nachdenken so ein Projekt zu betreiben, ausser zu reinem Hobby (und weil es der Frau gefällt).

Die Frage aus dem Thema hier kommt aus einem kleinen Problem was ich habe, weil ich halt nicht Krösus bin und für ein halbes Dutzend User auch nicht einsehe, warum ich einen Haufen Geld für einen "guten" Server bezahlen sollte.
Das Server-Ende meiner Software läuft auf einem vServer bei einem Billig-Provider der mich gerade mal 10€ im Monat kostet und das ist für einen Server mit Root-Zugriff und Windows-OS (coden für Linux kann ich halt nicht und will ich auch nicht mit anfangen) wohl ein echter Dumping-Preis.
Dabei ergeben sich halt Problemchen die es auf einem "gescheiten" Server nicht gäbe, wie z.B. dass der Server alle paar Minuten mal die Verbindung zu einzelnen Usern verliert und alle paar Stunden allen Usern gleichzeitig die Verbindung kappt.
Meine Frau und ihre Freunde sagen mir alle, kein Problem, dauert doch nur einen Buttonklick und weniger als eine Sekunde bis man wieder drin ist und passiert eh nur 1-2x am Tag, aber ich probiere halt gerade herum, ob und wie ich diese Verbindungsabbrüche vor den Usern "verstecken" kann, sprich im Hintergrund so schnell eine neue Verbindung aufbauen, dass weder der User noch sein Gegner merkt, dass er offline war.
Dazu starte ich halt einen neuen Thread der eine neue Verbindung aufbaut (ohne den alten Verbindungs-Thread zu beenden) und dann dem alten Verbindungs-Thread den neuen Socket mitteilt und damit der neue Thread am Ende weiss wem er den Socket geben soll übergebe ich diesem neuen Thread beim Start die User-ID des betroffenen Users, was wahlweise auch die Thread-ID der abgebrochenen Verbindung sein könnte.
Das klappt auch schon prima, dauerte mir nur einen Tick zu lange, sprich der User konnte merken, dass die Verbindung einen kleinen Schluckauf hatte und diese kurze Verzögerung habe ich jetzt beseitigt oder zumindest verkürzt, weil die direkte Übergabe der ID nun mal eine Winzigkeit schneller ist als der "korrekte" Weg.
 
Zuletzt bearbeitet:
Müsste es nicht mit memcpy standardkonform machbar sein?

Code:
#include <cstring>
#include <cstddef>
#include <iostream>

static_assert(sizeof(std::uint64_t) == sizeof(void*));

void threadfunc(void* args){
    std::uint64_t tmp;
    std::memcpy(&tmp, &args, sizeof(void*));
    unsigned int value = tmp;
    std::cout << value << std::endl;
}

int main(){

    unsigned int value = 42;
    std::uint64_t tmp = value;
    void* args;  
    std::memcpy(&args, &tmp, sizeof(std::uint64_t));
    threadfunc(args);
}

Edit: in main tmp statt value als source für memcpy benutzen!
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: BlackMark
Stimmt, mit memcpy kann man es standardkonform machen. In dem Fall gilt die Regelung für reinterpret_cast nicht, weil kein reinterpret_cast, und memcpy ist auch von strict aliasing ausgenommen. Ich sehe keinen Grund warum das nicht standardkonform sein soll.

Gruß
BlackMark
 
MarkP schrieb:
Da ist schon "Codebase" drastisch übertrieben.
Ich bin ein Hobby-Coder der noch nie irgendein Programm verkauft hat, sondern immer nur für den eigenen Bedarf und für umsonst macht.
Mir ist es völlig egal ob ich 100x länger an einem Projekt sitze weil ich so nahe es geht an der untersten Basis code und darum drastisch viel mehr zu tippen habe, denn es geht mir gar nicht so sehr um ein möglichst schnell erreichtes Endergebnis, mir geht es darum ein Hobby zu haben bei dem ich nun schon über 20 Jahre an einem einzelnen Projekt code.

Kleiner denkanstoß, da du schon auf Memory-Leaks hingewiesen wurdest.
Ich bin selbst von einigen Monaten von C++ zu Rust gewechselt, da ich nicht das Gefühl hatte jemals C++ lernen zu können bei allein 20 Varianten um eine Variable zu initialisieren.
Rust garantiert dier, dass es alle free() o.ä. aufrufe abnimmt, keine oder falsch initialisierte Geschichten per compiler erkennt und die ganzen Pointergeschichten durch Referenzen ersetzt, wodurch nullpointer exceptions entfallen. Ergo entfallen die meisten klassischen Probleme, inklusive Diskussionen bzgl. Ownership.

Ich schreibe das, da Rust und C Code interoperabel sind, dh. du könntest den C Code als teil deines Rust Projektes weiterführen und problematische stellen in Rust schreiben.
Wenn du es eh nur als hoby machst und dir die investierte Zeit egal ist wäre das vlt. ne interessante Alternative?
Nebenbei ist Web-dev. eines der 4 Zentralen Rust-Anwendungsgebiete, da gibt es sicher mehrere Bibliotheken, welche dir bei so einer klassischen Aufgabe wie verbindungsabbrüchen helfen.
 
  • Gefällt mir
Reaktionen: KitKat::new()
MarkP schrieb:
weil die direkte Übergabe der ID nun mal eine Winzigkeit schneller ist als der "korrekte" Weg.
Ich habe das mal für dich gebenchmarked:
Code:
copySpawner with 100000 iterations took: 6960ms which is 69us per iteration
memCpySpawner with 100000 iterations took: 6910ms which is 69us per iteration
heapCopySpawner with 100000 iterations took: 6982ms which is 69us per iteration

Wie du sehen kannst ist die Performance zwischen den verschiedenen Methoden exakt gleich. Der Grund dafür ist, dass das erzeugen eines Threads extrem teuer ist und der großteil der Arbeit darin besteht den Thread zu erzeugen. Im Vergleich dazu ist der Unterschied zwischen einem normalen Copy und einem Heap-Alloc so gering, dass es nicht einmal in der Messung aufscheint. Du hast natürlich prinzipiell recht, dass eine Heap-Allocation teurer ist, als den Wert einfach zu kopieren, aber in deinem Use-Case spielt das sowas von keine Rolle. Das Erzeugen des Threads kostet auf meinem System ~70 Mikrosekunden, das ist viele Zehnerpotenzen entfernt von etwas spürbarem.

Wenn du also Performance Probleme hast, dann liegt es an etwas anderem, aber ganz sicher nicht daran wie du deine Daten (ja, eine Zahl ist eine Form von Daten) an den Thread übergibst.

C++:
#include <chrono>
#include <iostream>
#include <memory>
#include <string_view>

#include <cstdint>
#include <cstring>

#include <windows.h>

//////////////////////////////////////////////////////////////////////////

DWORD WINAPI copyThread(LPVOID data)
{
    const auto val = reinterpret_cast<std::intptr_t>(data);
    return static_cast<DWORD>(val);
}

DWORD WINAPI memCpyThread(LPVOID data)
{
    std::intptr_t val;
    static_assert(sizeof(data) == sizeof(val));

    std::memcpy(&val, &data, sizeof(val));
    return static_cast<DWORD>(val);
}

DWORD WINAPI heapCopyThread(LPVOID data)
{
    const auto val = std::unique_ptr<std::intptr_t>(static_cast<std::intptr_t*>(data));
    return static_cast<DWORD>(*val);
}

//////////////////////////////////////////////////////////////////////////

static inline void copySpawner(std::intptr_t val)
{
    const auto data = reinterpret_cast<void*>(val);
    auto hThread = CreateThread(nullptr, 0, copyThread, data, 0, nullptr);
    WaitForMultipleObjects(1, &hThread, true, INFINITE);
    CloseHandle(hThread);
}

static inline void memCpySpawner(std::intptr_t val)
{
    void* data;
    static_assert(sizeof(data) == sizeof(val));
    std::memcpy(&data, &val, sizeof(val));
    auto hThread = CreateThread(nullptr, 0, memCpyThread, data, 0, nullptr);
    WaitForMultipleObjects(1, &hThread, true, INFINITE);
    CloseHandle(hThread);
}

static inline void heapCopySpawner(std::intptr_t val)
{
    auto hThread = CreateThread(nullptr, 0, heapCopyThread, new std::intptr_t{val}, 0, nullptr);
    WaitForMultipleObjects(1, &hThread, true, INFINITE);
    CloseHandle(hThread);
}

//////////////////////////////////////////////////////////////////////////

template<typename Fn>
void benchmark(Fn&& threadSpawner, std::string_view name, std::intptr_t numIterations)
{
    const auto timeStart = std::chrono::high_resolution_clock::now();

    for(std::intptr_t i = 0; i < numIterations; ++i) {
        threadSpawner(i);
    }

    const auto timeEnd = std::chrono::high_resolution_clock::now();

    std::cout << name << " with " << numIterations << " iterations took: ";
    std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(timeEnd - timeStart).count() << "ms";
    std::cout << " which is " << std::chrono::duration_cast<std::chrono::microseconds>((timeEnd - timeStart) / numIterations).count() << "us per iteration";
    std::cout << std::endl;
}

int main()
{
    benchmark(copySpawner, "copySpawner", 100'000);
    benchmark(memCpySpawner, "memCpySpawner", 100'000);
    benchmark(heapCopySpawner, "heapCopySpawner", 100'000);

    return 0;
}

Gruß
BlackMark
 
MarkP schrieb:
Vielleicht muss ich es NOCH einfacher erklären?
Ein Pointer ist doch genau wie ein UINT effektiv nur eine Zahl, nur mit verschiedenen Wertebereichen und zu verschiedenem Zweck.

Ich glaube wir müssen dir das NOCHmal viel einfacher erklären:
Du willst, dass die im C++-Standard beschriebene, abstrakte Maschine etwas für dich tut. Dann musst du dich auch an ihre Regeln halten, auch wenn du viel besser weißt, was Wertebereiche sind.

Hier sind soviel gute Antworten gefallen und es ist fast schon amüsant, wie du immer wieder einen neuen Grund findest, wieso alles so sein muss, wie du willst.
Ergänzung ()

Edit:

MarkP schrieb:
Deine Warnungen ala " nicht manchmal crashed oder mit einem anderen/neuen Compiler plötzlich nicht mehr funktioniert" kenne ich nun schon über die Jahre aus 100 anderen Unterhaltungen wo mir dies immer wieder erklärt wird.
Interessanterweise sind es aber die Projekte dieser Leute die immer wieder crashen, während mein Projekt seit über 10 Jahren keinerlei Bug mehr gehabt hat, ich habe das Server-Ende von meinem Gameserver schon über 2 Jahre am Stück laufen gehabt, ohne Neustart, ohne Bug, ohne Crash, schlicht wartungsfrei und das wird mit der neuen Funktion die ich da jetzt gerade einbauen will auch nicht anders werden.

Unter diesem Umständen würde ich dir einfach dazu raten, von int nach void* und wieder zurück im C-Style zu casten. Fertig. :)
Ergänzung ()

MarkP schrieb:
[...], weil die direkte Übergabe der ID nun mal eine Winzigkeit schneller ist als der "korrekte" Weg.

Weißt du eigentlich, wie lange das Erstellen eines Threads braucht?
 
Zuletzt bearbeitet:
Ich kann mich nur nochmals fuer die Features aussprechen, die ein halbwegs moderner C++ Standard mitbringt: std::thread oder std::async. Dann laeuft das ploetzlich auf allen Plattformen. Dass du keine Lust hattest, dich mit Linux-Programmierung auseinander zu setzen, wuerde dann einfach wegfallen, weil du bereits Linux- (und Mac, und, und und) kompatibel programmierst. Dein aktuelles Problem wurde geloest, alles paletti und klar will man wegen eines Details nicht die komplette Architektur seines Programms aendern. Aber wenn es ein Hobby ist und du etwas Neugier hast, kann ich dir sagen: Es lohnt sich. Du wirst viel sicherer programmieren koennen und jede Menge Zeilen Code los.
 
kuddlmuddl schrieb:
Für so technisch spezifische Fragen wie in deinem Fall ist https://stackoverflow.com/ viel besser, wenn man keine Diskussionen lesen will.

Stackoverflow ist scheiße.
Du musst so technisch spezifische Fragen erstellen, dass du, wenn du dazu in der Lage bist, die Antwort locker selber ergooglen kannst, oder keiner weiß auf SO die Antwort zu deiner Frage.
 
  • Gefällt mir
Reaktionen: KitKat::new()
Zurück
Oben