2010-12-03 6 views
11

Nehmen wir an, ich habe einen Vektor von N Elementen, aber bis zu n Elemente dieses Vektors haben aussagekräftige Daten. Ein Aktualisierungsthread aktualisiert das n-te oder n + 1te Element (setzt dann n = n + 1), prüft auch, ob n zu nah an N ist, und ruft vector :: resize (N + M) auf, falls erforderlich. Nach dem Aktualisieren ruft der Thread mehrere untergeordnete Threads auf, um bis zu n-ten Daten zu lesen und einige Berechnungen durchzuführen.STL-Vektor und Thread-Sicherheit

Es ist garantiert, dass untergeordnete Threads niemals Daten ändern oder löschen (tatsächlich werden überhaupt keine Daten gelöscht), und das Updater ruft untergeordnete Elemente direkt nach Abschluss der Aktualisierung auf.

Bis jetzt ist kein Problem aufgetreten, aber ich möchte fragen, ob ein Problem bei der Neuzuordnung des Vektors zu einem größeren Speicherblock auftreten kann, wenn noch einige untergeordnete Threads vom vorherigen Update übrig sind.
Oder ist es sicher, in einem solchen Multithread-Fall Vektor zu verwenden, da es nicht Thread-sicher ist?

EDIT: Da nur Einfügen erfolgt, wenn der Updater vector :: resize (N + M, 0) aufruft, gibt es mögliche Lösungen für mein Problem? Aufgrund der großen Leistungsfähigkeit des STL-Vektors bin ich nicht bereit, ihn durch einen sperrbaren Vektor zu ersetzen, oder in diesem Fall gibt es irgendwelche performanten, bekannten und sperrfreien Vektoren?

Antwort

19

Ich möchte fragen, ob ein Problem bei der Neuzuordnung von Vektor zu einem größeren Speicherblock auftreten kann, wenn noch einige untergeordnete Arbeitsfäden von der vorherigen Aktualisierung übrig sind.

Ja, das wäre sehr schlecht.

Wenn Sie einen Container aus mehreren Threads verwenden und mindestens ein Thread möglicherweise eine Aktion ausführt, die den Status des Containers ändern kann, muss der Zugriff auf den Container synchronisiert werden.

Im Falle von std::vector, alles, was seine Größe verändert (vor allem, Einfügungen und Löschungen) seinen Zustand ändern, selbst wenn eine Neuzuweisung nicht erforderlich ist (jede Einfügung oder Löschung erfordert std::vector ‚s interne Größe Buchhaltungsdaten aktualisiert werden) .


Eine Lösung für Ihr Problem wäre der Hersteller zu haben, dynamisch die std::vector zuweisen und eine std::shared_ptr<std::vector<T> > zu verwenden, es zu besitzen und diese std::shared_ptr zu jedem der Verbraucher geben. Wenn der Hersteller mehr Daten hinzufügen muss, kann er dynamisch eine neue std::vector mit einer neuen, größeren Größe und Kopien der Elemente aus der alten std::vector zuweisen. Wenn Sie dann neue Kunden ausspinnen oder die Verbraucher mit den neuen Daten aktualisieren, müssen Sie ihnen einfach eine std::shared_ptr für die neue std::vector geben.

+0

@James McNellis: Ja. Das ist ein guter Rat.Ich kann die Umverteilung selbst vornehmen. Tatsächlich sind Vektoren innerhalb einer Klasse eingeschlossen, die einen Zeiger auf Vektor enthält. Es ist nicht shared_ptr, aber ich kann leicht einen neuen größeren Vektor konstruieren, Elemente aus dem alten kopieren, löschen. Also, was ist der schnellste Weg, um einen großen Speicherblock zu kopieren. Kopiespeicher()? –

+1

Wäre es nicht einfacher, anstelle eines Vektors 'std :: deque' zu ​​verwenden? Das vermeidet die Neuzuweisungen vollständig und bietet dennoch eine Leistung, die fast gleichwertig mit Vektor ist. – jalf

+0

@jalf: Ich glaube nicht, dass es sicher ist, ein 'std :: deque' zu ​​verwenden, da Umplatzierungen nicht die einzige Sorge sind. Es gibt keine Garantie, dass 'std :: deque :: operator []' nicht die Größe oder eine der anderen Buchhaltungen innerhalb der 'Deque' überprüft. Es besteht also die Möglichkeit für eine Race-Bedingung, bei der ein Verbraucher 'operator' aufruft. ', der den internen Zustand liest, während der Erzeuger Daten hinzufügt, die den internen Zustand ändert. –

1

Wie entscheiden sich Ihre Mitarbeiter für die Arbeit mit Data Thread Safe? Gibt es irgendwelche Signale zwischen Arbeitern und Produzenten? Wenn nicht, gibt es definitiv ein Problem, bei dem der Produzent den Vektor bewegen könnte, während er noch bearbeitet wird. Dies könnte jedoch trivialerweise behoben werden, indem man stattdessen zu std::deque geht (beachten Sie, dass std::deque Iteratoren auf push_back ungültig macht, aber Verweise auf Elemente nicht betroffen sind).

+0

@stonemetal: Es gibt keine Signalisierung zwischen Arbeitern und Produzenten. Wie werde ich Deque benutzen? –

+0

@stonemetal: Ich füge keine Daten mit push_back() ein. Ich ändere die Größe und rufe 'vec [n] = X;' Ist das wichtig? –

+0

Es hängt davon ab, wie Sie den Arbeitern Arbeit geben. Du sagst, dass du nur bis zum Ende anhängst, also kannst du push_back einfach verwenden oder die Größe ändern, wenn nötig, dann Arbeit den Arbeitern per Index aushändigen, die sich nie ändert, da du weder Push oder Pop vorn oder Löschen, sondern auch Arbeit verteilen kannst durch einen Block von Zeigern, da Elemente garantiert werden, sich nicht zu bewegen, obwohl sie nicht zusammenhängend sind, so dass Sie einen Zeiger auf jedes Element benötigen würden, das ein Arbeiter benötigt, anstatt auf ein Array-Stil-Start und Längen-Setup. Iteratoren werden bei der Größenänderung ungültig gemacht, sodass sie keine Option darstellen. – stonemetal

Verwandte Themen