C ++异常是否足以实现线程本地存储? [英] Are C++ exceptions sufficient to implement thread-local storage?

查看:136
本文介绍了C ++异常是否足以实现线程本地存储?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是评论在一个答案中,线程本地存储是好的,并召回另一个关于例外的信息讨论我应该


$ b的唯一特别之处



$ p

$ p

两个和两个在一起,不会在其主要功能的函数catch块中执行一个完整的线程,它使用线程本地存储?



似乎工作好,虽然缓慢。这个小说还是很有特色的?是否有另一种解决问题的方法?我的初始前提是否正确?您的平台上有哪些开销 get_thread ?有什么可以优化?

  #include< iostream> 
#include< pthread.h>
使用命名空间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();

返回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;
}

这需要定义从 thread_exception_base派生的新异常类,使用 get_thread()初始化基数,但总体来说这不会是一个无效的失眠星期天早上...



编辑:看起来GCC使三个调用 pthread_getspecific get_thread 编辑:和很多讨厌的内省到堆栈,环境和可执行格式,以找到第一个演练我想念的 catch 块。这看起来与平台有关,因为GCC从操作系统调用一些 libunwind 。开销4000次循环。我想它也必须遍历类层次结构,但是可以被控制。

解决方案

以问题的俏皮精神,我提供这个可怕的噩梦创作:

  class tls 
{
void push(void * ptr)
{
//分配一个字符串来存储hex ptr
//和它自己的地址的十六进制
char * str = new char [100];
sprintf(str,|%x |%x,ptr,str);
strtok(str,|);
}

template< class Ptr>
Ptr * next()
{
//检索下一个指针标记
返回reinterpret_cast< Ptr *>(strtok(0,|),0, 16));
}

void * pop()
{
//检索(忘记)先前存储的指针
void * ptr = next< void> ();
delete [] next< char>();
return ptr;
}

//私有构造函数/析构函数
tls(){push(0); }
〜tls(){pop(); }

public:
static tls& singleton()
{
static tls i;
return i;
}

void * set(void * ptr)
{
void * old = pop();
push(ptr);
返回旧;
}

void * get()
{
//忘记并恢复每个访问
void * ptr = pop();
push(ptr);
return ptr;
}
};

利用这个事实,根据C ++标准, strtok 隐藏其第一个参数,以便后续调用可以通过 0 从相同的字符串中检索更多的令牌,因此在线程感知实现中必须使用TLS。

  example * e = new example; 

tls :: singleton()。set(e);

example * e2 = reinterpret_cast< example *>(tls :: singleton()。get());

只要 strtok 不使用在程序中其他任何地方,我们还有另一个备用的TLS插槽。


I was commenting on an answer that thread-local storage is nice and recalled another informative discussion about exceptions where I supposed

The only special thing about the execution environment within the throw block is that the exception object is referenced by rethrow.

Putting two and two together, wouldn't executing an entire thread inside a function-catch-block of its main function imbue it with thread-local storage?

It seems to work fine, albeit slowly. Is this novel or well-characterized? Is there another way of solving the problem? Was my initial premise correct? What kind of overhead does get_thread incur on your platform? What's the potential for optimization?

#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;
}

This does require defining new exception classes derived from thread_exception_base, initializing the base with get_thread(), but altogether this doesn't feel like an unproductive insomnia-ridden Sunday morning…

EDIT: Looks like GCC makes three calls to pthread_getspecific in get_thread. EDIT: and a lot of nasty introspection into the stack, environment, and executable format to find the catch block I missed on the first walkthrough. This looks highly platform-dependent, as GCC is calling some libunwind from the OS. Overhead on the order of 4000 cycles. I suppose it also has to traverse the class hierarchy but that can be kept under control.

解决方案

In the playful spirit of the question, I offer this horrifying nightmare creation:

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;
    }
};

Taking advantage of the fact that according to the C++ standard, strtok stashes its first argument so that subsequent calls can pass 0 to retrieve further tokens from the same string, so therefore in a thread-aware implementation it must be using TLS.

example *e = new example;

tls::singleton().set(e);

example *e2 = reinterpret_cast<example *>(tls::singleton().get());

So as long as strtok is not used in the intended way anywhere else in the program, we have another spare TLS slot.

这篇关于C ++异常是否足以实现线程本地存储?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆