Sind C ++ Ausnahmen ausreichend lokalen Thread-Speicher zu implementieren?
-
21-09-2019 - |
Frage
Ich war kommentieren auf eine Antwort diesem Thread-lokalen Speicher ist schön und erinnerte an eine andere informative Diskussion über Ausnahmen, bei denen ich annehmen
Das einzige Besondere an der Ausführungsumgebung innerhalb des throw Block ist, dass das Ausnahmeobjekt ist verwiesen durch rethrow.
Setzen von zwei und zwei zusammen, würde nicht eine gesamte Faden innerhalb einer Funktion-catch-Block von seiner Hauptfunktion Imbue mit gewinde lokalen Speicher ausführt?
Es scheint gut zu funktionieren, wenn auch langsam. Ist das neue oder gut charakterisierte? Gibt es eine andere Möglichkeit, das Problem zu lösen? War meine erste Prämisse richtig? Welche Art von Overhead tut get_thread
incur auf Ihrer Plattform? Was ist das Optimierungspotenzial?
#include <iostream>
#include <pthread.h>
using namespace std;
struct thlocal {
string name;
thlocal( string const &n ) : name(n) {}
};
struct thread_exception_base {
thlocal &th;
thread_exception_base( thlocal &in_th ) : th( in_th ) {}
thread_exception_base( thread_exception_base const &in ) : th( in.th ) {}
};
thlocal &get_thread() throw() {
try {
throw;
} catch( thread_exception_base &local ) {
return local.th;
}
}
void print_thread() {
cerr << get_thread().name << endl;
}
void *kid( void *local_v ) try {
thlocal &local = * static_cast< thlocal * >( local_v );
throw thread_exception_base( local );
} catch( thread_exception_base & ) {
print_thread();
return NULL;
}
int main() {
thlocal local( "main" );
try {
throw thread_exception_base( local );
} catch( thread_exception_base & ) {
print_thread();
pthread_t th;
thlocal kid_local( "kid" );
pthread_create( &th, NULL, &kid, &kid_local );
pthread_join( th, NULL );
print_thread();
}
return 0;
}
Dies erfordert neue Ausnahmeklassen definiert, von thread_exception_base
abgeleitet, die Basis mit get_thread()
initialisiert, aber insgesamt das fühlt sich nicht wie eine unproduktive Schlaflosigkeit geplagten Sonntagmorgen ...
EDIT: Sieht aus wie GCC macht drei Anrufe pthread_getspecific
in get_thread
. EDIT: und viele böse Innerlichkeit in den Stapel, Umwelt und ausführbare Format des catch
Block I auf dem ersten Durchlauf verpasst zu finden. Das sieht sehr plattformabhängig, wie GCC einige libunwind
aus dem O ruft. Overhead in der Größenordnung von 4000 Zyklen. Ich nehme an, es muss auch die Klassenhierarchie durchlaufen, aber das kann unter Kontrolle gehalten werden.
Lösung
Im spielerischen Geist der Frage, biete ich diese schreckliche Alptraum Schöpfung:
class tls
{
void push(void *ptr)
{
// allocate a string to store the hex ptr
// and the hex of its own address
char *str = new char[100];
sprintf(str, " |%x|%x", ptr, str);
strtok(str, "|");
}
template <class Ptr>
Ptr *next()
{
// retrieve the next pointer token
return reinterpret_cast<Ptr *>(strtoul(strtok(0, "|"), 0, 16));
}
void *pop()
{
// retrieve (and forget) a previously stored pointer
void *ptr = next<void>();
delete[] next<char>();
return ptr;
}
// private constructor/destructor
tls() { push(0); }
~tls() { pop(); }
public:
static tls &singleton()
{
static tls i;
return i;
}
void *set(void *ptr)
{
void *old = pop();
push(ptr);
return old;
}
void *get()
{
// forget and restore on each access
void *ptr = pop();
push(ptr);
return ptr;
}
};
Vorteil der Tatsache, dass nach dem C ++ Standard, strtok
stashes erstes Argument, so dass nachfolgende Aufrufe 0
passieren können weitere Token aus der gleichen Zeichenfolge abzurufen, so deshalb in einem Thread-aware Implementierung es TLS verwenden müssen.
example *e = new example;
tls::singleton().set(e);
example *e2 = reinterpret_cast<example *>(tls::singleton().get());
Also, solange strtok
nicht in die vorgesehenen Art und Weise verwendet wird, irgendwo anders im Programm haben wir eine weiteren Ersatz TLS-Steckplatz.
Andere Tipps
Ich glaube, Sie auf etwas sind hier. Dies könnte auch eine tragbare Art und Weise sein, Daten in Rückrufe zu erhalten, die nicht über einen Benutzer „Zustand“ Variable akzeptieren, wie Sie erwähnt haben, auch abgesehen von jeder expliziten Verwendung von Threads.
So klingt es wie Sie die Frage in Ihrem Thema beantwortet haben. YES
void *kid( void *local_v ) try {
thlocal &local = * static_cast< thlocal * >( local_v );
throw local;
} catch( thlocal & ) {
print_thread();
return NULL;
}
==
void *kid (void *local_v ) { print_thread(local_v); }
Ich könnte hier etwas fehlt, aber es ist kein Thread-Lokalspeicher, nur unnötig kompliziert Argument. Argument ist für jeden Thread nur, weil es zu pthread_create geführt wird, nicht wegen irgendeiner Ausnahme Jonglieren.
Es stellte sich heraus, dass ich in der Tat fehlte, dass GCC tatsächlichen lokalen Threadspeicher Anrufe in diesem Beispiel produziert. Es macht tatsächlich die Frage interessant. Ich bin noch nicht ganz sicher, ob es ein Fall für andere Compiler ist und wie unterscheidet es sich von Aufruf Fadenspeicher direkt an.
Ich stehe nach wie vor durch mein allgemeines Argument, dass die gleichen Daten in einer einfacheren und geradlinig Art und Weise zugegriffen werden können, wäre es Argumente, Stapel zu Fuß oder Gewinde lokale Speicher.
Daten über den aktuellen Funktion Call-Stack Zugriff wird immer Thread-sicher. Deshalb Code Thread-sicher ist, nicht wegen des geschickten Einsatzes von Ausnahmen. Fädeln lokalen Speicher ermöglicht es uns, Speicher pro Thread Daten und verweisen sie außerhalb des unmittelbaren Call-Stack.