Ist es mit Thread-sicher, STD :: Atomic <bool> zu verwenden, um den Zugriff auf eine Datenstruktur kooperativ mit nur LaC++

Programme in C++. Entwicklerforum
Anonymous
 Ist es mit Thread-sicher, STD :: Atomic <bool> zu verwenden, um den Zugriff auf eine Datenstruktur kooperativ mit nur La

Post by Anonymous »

Ich spiele mit schlossloser C ++-Programmierung herum und habe eine Methode zur sicheren (?) Übertragung des Eigentums an einer nicht-thread-sicheren Datenstruktur (

Code: Select all

std::unordered_map
In meinem trivialen Beispiel) von einem Produzenten -Thread zu einem Verbraucher -Thread nur mit einem gemeinsam genutzten std :: atomic für die Synchronisation. Es scheint, als ob es thread-sicher sein sollte, aber die Intuition ist in der Programmierung locker ohne zuverlässig. Ich suche also nach Gründen, warum dieser Ansatz in der Praxis möglicherweise nicht mit Thread-sicher ist. Iteriert über den Inhalt des zurückgegebenen std :: uncondeded_map . Dies kann dies sicher tun, weil die zurückgegebene std :: uncondeded_map (

Code: Select all

_consumerThreadUpdatesTable
) ist nur zum Verbraucher -Thread zugänglich; Der Produzenten -Thread berührt es nie.

Code: Select all

_producerThreadUpdatesTable
), und dann kann es ProducerPushUpDatestoconsumer () aufrufen, um zu versuchen, den Inhalt seiner privaten Tabelle in die Tabelle mit Shared-Access (-

Code: Select all

_bridgeUpdatesTable
). Der Produzenten -Thread ruft auch ProducerPushUpDatestoconsumer () in regelmäßigen Abständen, nur für den Fall, dass ein früherer Anruf die Updates erfolgreich vorantreiben konnte. std :: atomic . Wenn diese Variable wahr ist, kann nur der Verbraucher-Thread auf _BridgeUpDatestable zugreifen, oder wenn diese Variable falsch ist, kann nur der Produzenten-Thread auf _bridgeUpDatestable zugreifen. Es wird niemals einseitig Zugriff für sich selbst erfassen. _consumerownsBridgeUpdatestable.load () == true , dieser Status bleibt auf unbestimmte Zeit gültig - und es gibt daher eine Garantie dafür, dass _BridgeUpdatestable geändert wird, während ConsumerConEUpdates () darauf zugreift. _consumerownsBridgeUpdatestable = true; , sodass der Produzenten -Thread auf die Tatsache beruhen kann, dass, wenn _consumerownsbridgeUpdatestable ProcesserPushUpDatestoconsumer () greift darauf zu. Der Code scheint gut zu funktionieren, aber der buggy locklose Code tut es oft. Vermisse ich gotchas? < /P>

Code: Select all

 #include 
#include 

// This object lets us safely transport sets of key-value pair updates from a single ProducerThread to a single ConsumerThread without requiring any Mutex locking
class KeyedParameterBridge
{
public:
KeyedParameterBridge() : _consumerOwnsBridgeUpdatesTable(false)
{
// empty
}

/** Called by the producer thread; places a pending update into the producer-thread's local pending-update-table.  */
void ProducerPutUpdate(int parameterID, float parameterValue) {_producerThreadUpdatesTable[parameterID] = parameterValue;}

/** Called by the producer thread; attempts to push all of the updates currently placed (via ProducerPutUpdate()) to
*  the _bridgeUpdatesTable where the consumer thread will be able to access them
*  @returns true if we were able to push some updates in this call, or false if didn't push any updates
*/
bool ProducerPushUpdatesToConsumer()
{
if (_producerThreadUpdatesTable.size() == 0) return false;  // no updates to push
if (_consumerOwnsBridgeUpdatesTable.load())  return false;  // can't safely do anything yet; we'll have to wait until the ConsumerThread consumes _bridgeUpdatesTable's current contents

_bridgeUpdatesTable = std::move(_producerThreadUpdatesTable);  // O(1) pointer-move from _producerThreadUpdatesTable to _bridgeUpdatesTable
_consumerOwnsBridgeUpdatesTable = true;                        // so the ConsumerThread will know the _bridgeUpdatesTable is safe for him to access now
_producerThreadUpdatesTable.clear();                           // make sure our producer-side table is in a known-empty state
return true;
}

/** Called periodically by the consumer thread; returns any available updates for the consumer thread to apply to its local data structure */
const std::unordered_map & ConsumerConsumeUpdates()
{
_consumerThreadUpdatesTable.clear(); // make sure our consumer-side table is in a known-empty state

if (_consumerOwnsBridgeUpdatesTable.load())  // see if we have access
{
_consumerThreadUpdatesTable = std::move(_bridgeUpdatesTable); // O(1) pointer-move from _bridgeUpdatesTable to _consumerThreadUpdatesTable
_consumerOwnsBridgeUpdatesTable = false;                      // so the ProducerThread will know we already grabbed the update and he can push again now
}
return _consumerThreadUpdatesTable;
}

private:
std::atomic _consumerOwnsBridgeUpdatesTable; // true iff the _bridgeUpdatesTable currently contains data for the consumer thread to consume

std::unordered_map _producerThreadUpdatesTable;  // populated by the producer-thread, never accessed by the consumer thread
std::unordered_map _bridgeUpdatesTable;          // access to this table is governed by the state of (_consumerOwnsBridgeUpdatesTable)
std::unordered_map _consumerThreadUpdatesTable;  // read by the consumer-thread, never accessed by the producer thread
};

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post