Stimmt es, dass nur ein einzelner Thread die Linearisierbarkeit garantieren kann?C++

Programme in C++. Entwicklerforum
Anonymous
 Stimmt es, dass nur ein einzelner Thread die Linearisierbarkeit garantieren kann?

Post by Anonymous »

Betrachten Sie dieses Beispiel:

Code: Select all

#include 
#include 
#include 
int main() {
std::atomic key;
auto t1 = std::thread([&]() {
auto now = std::chrono::steady_clock::now();
auto duration = now.time_since_epoch();
auto seconds =
std::chrono::duration_cast(duration).count();
key.store(seconds);  // #1 occurred at timepoint `T1`
});
auto t2 = std::thread([&]() {
auto now = std::chrono::steady_clock::now();
auto duration = now.time_since_epoch();
auto seconds =
std::chrono::duration_cast(duration).count();
key.store(seconds); // #2 occurred at timepoint `T2`
});
auto t3 = std::thread([&]() {
std::this_thread::sleep_for(std::chrono::milliseconds(300)); // debounce
auto search_key = key.load(); // #3 occurred at timepoint `T3`
// request(search_key);
});
t1.join();
t2.join();
t3.join();
}
Angenommen, dass die Eingaben jedes Benutzers einen Thread erstellen und die Eingaben des Benutzers berechnen, um einen Suchschlüssel zu erstellen, und diesen dann in den Schlüssel schreiben, gibt es einen Thread, der die Eingaben des Benutzers liest und eine Netzwerkanforderung für den geladenen Schlüssel stellt. Um häufige Netzwerkanfragen während der Eingabe des Benutzers zu vermeiden, gibt es auch einen Entprellungsmechanismus im Suchthread.
In diesem Beispiel werden die beiden Eingabeereignisse des Benutzers modelliert, nämlich t1 und t2. Angenommen, jedes Ereignis trat zu dem im Kommentar angegebenen Zeitpunkt auf und T1 < T2 < T3. Auch wenn jede atomare Operation seq_cst verwendet und eine einzige Gesamtreihenfolge bildet und die Zeitpunkte T1 und T2 beide vor T3 liegen, kann #3 immer noch #1 laden und vor #2 kohärenzgeordnet sein.
Dies ist keine Linearisierbarkeit, da T1 < T2 < T3 und die Ladung bei T3 den Speicher nicht gelesen haben das geschah bei T2. (Ich bin mir nicht sicher, ob die Konsistenz in der Zeitleiste als Linearisierbarkeit bezeichnet werden kann).
Ist es also aus Sicht des C++-Standards wahr, dass nur ein einzelner Thread Linearisierbarkeit garantieren kann? Das heißt, der Ladevorgang liest garantiert immer den Speicher, der vor dem Ladevorgang am nächsten an der Zeitachse stattfand.
Update-Beispiel

Code: Select all

#include 
#include 
#include 

int main() {
std::atomic key;
auto t3 = std::thread([&]() {
std::this_thread::sleep_for(std::chrono::milliseconds(300)); // debounce
auto search_key = key.load(); // C
// request(search_key);
if(search_key==key.load()){ // D
// use the response
}
});
key.store(1); // A first input
key.store(2); // B second input
t3.join();
}

Code: Select all

A
und B bezeichnen das Doppelte der schnellen Eingabe des Benutzers. Offensichtlich bezeichnet B die letzte Eingabe des Benutzers, C versucht, die letzte Eingabe des Benutzers nach einer Entprellzeit zu lesen und eine Netzwerkanforderung im Thread t3 auszuführen. Es ist nicht garantiert, dass C gemäß dem C++-Standard aus B liest.
Der vollständige Fall besteht darin, eine Suchaufgabe für jede Eingabe des Benutzers zu erstellen und die Antwort zu verwerfen, wenn sich der Wert geändert hat, um zu modellieren, was in Javascript geschieht

Code: Select all

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

// =================================================================
// 1. TASK DISPATCHER (The "Event Loop")
// This class mimics the JavaScript Task Queue. It allows background
// threads to "post" work back to the main thread for execution.
// =================================================================
class Dispatcher {
std::queue tasks;
std::mutex mtx;

public:
// Pushes a function into the queue.  Safe to call from any thread.
void post(std::function task) {
std::lock_guard lock(mtx);
tasks.push(task);
}

// Executes all pending tasks. Should be called by the Main Thread.
void processEvents() {
std::queue toExecute;
{
// Swap content to minimize lock time (prevents blocking posters)
std::lock_guard lock(mtx);
std::swap(toExecute, tasks);
}
while (!toExecute.empty()) {
toExecute.front()(); // Execute the callback
toExecute.pop();
}
}
};

Dispatcher g_dispatcher; // Global dispatcher acting as our event loop

// =================================================================
// 2. MOCK NETWORK FUNCTION
// Simulates an asynchronous network request.
// =================================================================
void fetchNetworkData(int inputValue, std::function callback) {
// Start a background thread to simulate network latency
std::thread([inputValue, callback]() {
// Simulate a 2-second delay
std::this_thread::sleep_for(std::chrono::seconds(2));

std::string result = "Data_for_" + std::to_string(inputValue);

// IMPORTANT: Instead of running the callback here (in the background thread),
// we post it back to the dispatcher so it runs on the Main Thread.
g_dispatcher.post([callback, result]() {
callback(result);
});
}).detach();
}

// =================================================================
// 3. SEARCH COMPONENT
// Handles the core logic: capturing input and validating responses.
// =================================================================
class SearchComponent {
int m_latestValue = -1; // State: holds the most recent input from the user

public:
void onUserInput(int newValue) {
m_latestValue = newValue;
std::cout

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post