继承 pthread 本地数据

huangapple go评论85阅读模式
英文:

Inherit pthread local data

问题

我一直在尝试查看pthread是否有一种方法可以继承线程本地数据,就像Java中的InheritableThreadLocal一样。

我的意思是,如果线程A具有键my_key = 2,然后创建线程B,那么线程B也将具有相同的键my_key = 2

在我的回调函数中,我需要一个特定的标识符来知道回调的来源线程,但它们由库在另一个线程中运行,我不知道如何做到这一点。

提前感谢。

英文:

I have trying to see if there is in pthread a way to inherit thread local data,like InheritableThreadLocal in Java.

What I mean by that is that if Thread A, with a key my_key = 2 spawns a Thread B, then Thread B is spawned with the same key my_key = 2.

void callback(...) {...}

main()


library_set_callback_on_data_retrieved(callback)

--- Start thread A ---> call_black_box_library(library_specific_params...) ---> calls callback in another_thread

--- Start thread B ---> call_black_box_library(library_specific_params...) --> calls callback in another_thread

In my callback, I would need a specific identifier to know from which thread the callback originates, but they are ran in another thread by the library and I do not know how to do that.

Thanks in advance

答案1

得分: 0

Pthreads允许通过void*指针将数据传递给新线程的主例程,详见文档

您可以使用此机制将值传递给新线程。如果有更多数据需要发送,请创建一个包含所有数据的结构体;如果只有一个值,您可以将其强制转换为void*。一旦新线程启动,它将只需将该值写入变量 - 假设它被声明为thread_local。代码可能如下所示:

thread_local int m_key; // 或其他类型

// 如果您的其他类型不适合放入void*,则会失败:
char compileTimeCheck[1 - 2*(sizeof(int) > sizeof(void*))];

void* threadRoutine(void* p)
{
    m_key = (int)(intptr_t)p;

    // ...

    return NULL; // 或其他有意义的返回值
}

int main(void)
{
    m_key = 2;

    // ...

    pthread_t thread;
    if(pthread_create(&thread, NULL, &threadRoutine, (void*)(intptr_t)(m_key)) != 0)
    {
        // TODO:错误处理
    }
    else
    {
        // 线程正常

        // ...

        pthread_join(thread, NULL); // 或指向void*变量的指针,如果thread_routine有有意义的返回值
    }

    return 0;
}

godbolt上进行演示。

如果要提供的值多于能适应void*指针的值,那么我建议使用结构体,并可能向新线程传递其副本:

struct Keys
{
    // 所有键
};

thread_local struct Keys m_allKeys = {/*...*/};

void* threadRoutine(void* p)
{
    memcpy(&m_allKeys, p, sizeof(m_allKeys));
    free(p);

    // ...

    return NULL;
}

int main(void)
{
    // ...

    void* keys = malloc(sizeof(m_allKeys));
    if(!keys)
    {
        // TODO:错误处理
    }
    else
    {
        memcpy(keys, &m_allKeys, sizeof(m_allKeys));

        pthread_t thread;
        if(pthread_create(&thread, NULL, &threadRoutine, keys) != 0)
        {
            free(keys); // 避免内存泄漏!
            // TODO:错误处理
        }
        else
        {
            // 线程正常

            // ...

            pthread_join(thread, NULL);
        }
    }

    return 0;
}

godbolt上进行演示。

附注:如果要避免复制并直接传递&m_allKeys,则需要通过适当的方式避免新线程在复制数据之前和旧线程在通过某种适当的方式创建线程后再次开始修改数据之间的竞争条件。

英文:

Pthreads allow to forward data to the new threads main routine via a void* pointer, see documentation.

You can use this mechanism to pass the value to the new thread. If there's further data to be sent create a struct containing all data, if there's only the value you might just cast it to void*. Once the new thread has started it would just write the value to the variable – assuming it is declared thread_local. This might look as follows:

thread_local int m_key; // or whatever other type

// fails if your whatever other type doesn't fit into a void*:
char compileTimeCheck[1 - 2*(sizeof(int) > sizeof(void*))];

void* threadRoutine(void* p)
{
    m_key = (int)(intptr_t)p;

    // ...

    return NULL; // or whatever meaningful
}

int main(void)
{
    m_key = 2;

    // ...

    pthread_t thread;
    if(pthread_create(&thread, NULL, &threadRoutine, (void*)(intptr_t)(m_key)) != 0)
    {
        // TODO: error handling
    }
    else
    {
        // thread OK

        // ...

        pthread_join(thread, NULL); // or pointer to a void* variable if thread_routine
                                    // has something meaningful to return
    }

    return 0;
}

Demonstration on godbolt.

If there are more values to be provided than fit into a void* pointer then I'd recommend a struct and possibly passing copies of to the new thread:

struct Keys
{
    // whatever keys
};

thread_local struct Keys m_allKeys = {/*...*/};

void* threadRoutine(void* p)
{
    memcpy(&m_allKeys, p, sizeof(m_allKeys));
    free(p);

    // ...

    return NULL;
}

int main(void)
{
    // ...

    void* keys = malloc(sizeof(m_allKeys));
    if(!keys)
    {
        // TODO: error handling
    }
    else
    {
        memcpy(keys, &m_allKeys, sizeof(m_allKeys));

        pthread_t thread;
        if(pthread_create(&thread, NULL, &threadRoutine, keys) != 0)
        {
            free(keys); // avoid memory leak!
            // TODO: error handling
        }
        else
        {
            // thread OK

            // ...

            pthread_join(thread, NULL);
        }
    }

    return 0;
}

Demonstration on godbolt.

Side note: If you want to avoid a copy and pass &m_allKeys directly you need to avoid race conditions in between the new thread still copying the data and the old thread starting to modify it again after thread creation by some appropriate means.

答案2

得分: 0

我一直在尝试查看pthread中是否有一种方法可以继承线程本地数据,就像Java中的InheritableThreadLocal一样。

TL;DR:不,pthreads不提供类似于InheritableThreadLocal的功能。与Java不同,它通常不将线程的启动与哪个线程有关。

最接近的pthreads功能是线程特定数据,其主要接口包括pthread_key_create()pthread_setspecific()和pthread_getspecific(),以及pthread_key_delete()。关于pthread_key_create()的文档解释了:

在创建密钥时,所有活动线程将与新密钥关联的值设置为NULL。在创建线程时,新线程将在所有定义的密钥中关联值NULL。

没有记录异常,因此,此功能不允许一个线程从另一个线程继承线程特定数据。

再进一步说,自从C11以来,C语言已经内置了对线程的支持,包括一组与pthreads紧密类似的函数。其与上述pthread函数类似的函数是tss_create()tss_set()tss_get()tss_delete(),它们在与线程特定存储的(非)继承方面具有相同的语义。

C11还定义了一种新的“线程”存储期,通过新的_Thread_local存储类说明符指定。对于大多数情况,这是进入线程本地数据的更简单方式。但是,此功能也不允许从一个线程继承数据到另一个线程。每个线程本地对象在创建其线程时初始化,要么初始化为其初始化程序指定的值,要么如果没有初始化程序,则初始化为近似全零的默认值。

在我的回调函数中,我需要一个特定的标识符来知道回调从哪个线程发起,但它们由库在另一个线程中运行,我不知道该怎么做。

您的回调函数可以使用线程本地数据的任何形式来识别由同一线程执行的该回调或其他回调的多次调用,并在这些调用之间共享数据,但仅在单线程的基础上。这些机制不提供将一个线程的回调调用与任何其他线程的回调调用关联起来的功能。

英文:

> I have trying to see if there is in pthread a way to inherit thread local data,like InheritableThreadLocal in Java.

TL;DR: No, pthreads does not offer anything analogous to InheritableThreadLocal. Nor does it in general attribute significance to which thread starts which, unlike Java.

The closest pthreads feature is thread-specific data, the primary interfaces to which are pthread_key_create(), pthread_setspecific() and pthread_getspecific(), and pthread_key_delete(). The documentation for pthread_key_create() explains that

> Upon key creation, the value NULL shall be associated with the new key in all active threads. Upon thread creation, the value NULL shall be associated with all defined keys in the new thread.

No exceptions are documented, so no, this feature does not provide for one thread to inherit thread-specific data from another.

Going a bit further afield, since C11, the C language has had built-in support for threads, including a set of functions closely analogous to pthreads. Its analogs to the pthreads functions discussed above are tss_create(), tss_set(), tss_get(), and tss_delete(), and they have the same semantics with respect to (non-)inheritance of thread-specific storage.

C11 also defines a new "thread" storage duration, specified via the new _Thread_local storage class specifier. For most purposes, this is a simpler entry into thread-local data. But this feature does not provide for inheriting data from one thread to another either. Each thread-local object is initialized when its thread is created, either to the value specified by its initializer, or if it has no initializer then to a default value that, approximately speaking, is all-zero.

> In my callback, I would need a specific identifier to know from which
> thread the callback originates, but they are ran in another thread by
> the library and I do not know how to do that.

Your callback could use any of those forms of thread-local data to recognize that multiple calls to that or other callbacks are performed by the same thread, and to share data among those calls, but only on a single-thread basis. These mechanisms do not provide for relating the callback calls of one thread to those of any other.

huangapple
  • 本文由 发表于 2023年6月22日 18:20:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/76530894.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定