简体   繁体   中英

How to use Thread-specific data correctly

I am programming using pthread. I need a global variable that it has different value for different threads. And threads will use same function to deal with this variable, such as change its value. If one thread change its value, the value in other threads would not be changed. So I tried to use thread specific data, and wrote a example. I need wrap the pthread operations in functions. For exapmle: setspecific(), changedata, printdata(), create_key(), delete_key(), etc.

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

pthread_key_t key;
pthread_key_t key2;

struct test_struct {
    int i;
    float k;
}struct_data;

int temp;

int setspecificvar () { /* Set specific data for threads */

    pthread_setspecific (key, &struct_data);
    pthread_setspecific (key2, &temp);

    return 0;
}
int changedata (int i, float k, int tempvar) { /* Change specific data for threads */

    temp = tempvar;
    struct_data.i = i;
    struct_data.k = k;

    return 0;
}

int printdata (int t) {  /* print specific data for threads */

    printf ("The addres in child%d returned from pthread_getspecific(key):0x%p\n",                           \
            t, (struct test_struct *)pthread_getspecific(key));

    printf ("The value of members in structure bound to \"key\" in  child%d:\nstruct_data.i:%d\nstruct_data.k: %f\n", \
            t, ((struct test_struct *)pthread_getspecific (key))->i,                            \
            ((struct test_struct *)pthread_getspecific(key))->k);

    printf ("------------------------------------------------------\n");

    printf ("The addres in child%d returned from pthread_getspecific(key2):0x%p\n",                          \
            t, (int *)pthread_getspecific(key2));
    printf ("The value of \"temp\" bound to \"key2\" in child%d:%d\n", \
            t, *((int *)pthread_getspecific(key2)));

    return 0;
}

void *child1 (void *arg)
{
    setspecificvar ();
    changedata(10, 3.141500, 110); /* Should not change the data in child2 */
    printdata(1);
}

void *child2 (void *arg)
{
    /* sleep (2); */
    setspecificvar ();

    changedata(12, 2.141500, 120); /* Should not change the data in child1 */
    printdata(2);

    changedata (122, 22.141500, 1220); /* Should not change the data in child1 */
    printdata (2);
}

int create_key () {
    pthread_key_create (&key, NULL);
    pthread_key_create (&key2, NULL);
    return 0;
}

int delete_key () {

    pthread_key_delete (key);
    pthread_key_delete (key2);
    return 0;
}

int main (void)
{
    pthread_t tid1, tid2;

    create_key ();
    pthread_create (&tid1, NULL, (void *)child1, NULL);
    pthread_create (&tid2, NULL, (void *)child2, NULL);
    pthread_join (tid1, NULL);
    pthread_join (tid2, NULL);

    delete_key ();

    return 0;
}

I create two threads. When I make one thread sleep 2 seconds. I get correct answer.

The addres in child1 returned from pthread_getspecific(key):0x0x8049c98
The value of members in structure bound to *"key" in  child1*:
*struct_data.i:10
struct_data.k: 3.141500*
------------------------------------------------------
The addres in child1 returned from pthread_getspecific(key2):0x0x8049ca0
The value of "temp" bound to *"key2" in child1*:110
The addres in child2 returned from pthread_getspecific(key):0x0x8049c98
The value of members in structure bound to "key" in  child2:
struct_data.i:12
struct_data.k: 2.141500
------------------------------------------------------
The addres in child2 returned from pthread_getspecific(key2):0x0x8049ca0
The value of "temp" bound to "key2" in child2:120
The addres in child2 returned from pthread_getspecific(key):0x0x8049c98
The value of members in structure bound to "key" in  child2:
struct_data.i:122
struct_data.k: 22.141500
------------------------------------------------------
The addres in child2 returned from pthread_getspecific(key2):0x0x8049ca0
The value of "temp" bound to "key2" in child2:1220

when I comment /* sleep(2); */ , I get uncorrect answer.

The addres in child1 returned from pthread_getspecific(key):0x0x8049c54
The addres in child2 returned from pthread_getspecific(key):0x0x8049c54
The value of members in structure bound to "key" in  child2:
*struct_data.i:12
struct_data.k: 2.141500*
The value of members in structure bound to *"key" in  child1*:
struct_data.i:12
struct_data.k: 2.141500
------------------------------------------------------
The addres in child1 returned from pthread_getspecific(key2):0x0x8049c5c
The value of "temp" bound to *"key2" in child1*:120
------------------------------------------------------
The addres in child2 returned from pthread_getspecific(key2):0x0x8049c5c
The value of "temp" bound to "key2" in child2:120
The addres in child2 returned from pthread_getspecific(key):0x0x8049c54
The value of members in structure bound to "key" in  child2:
struct_data.i:122
struct_data.k: 22.141500
------------------------------------------------------
The addres in child2 returned from pthread_getspecific(key2):0x0x8049c5c
The value of "temp" bound to "key2" in child2:1220

I want get the right result without sleep a thread. One thread should not wait for the other thread to finish to call pthread_setspecific() , right? What should I do? Thanks for your consideration.Did I right to define struct_data as global variable? Anyone can help me?

int setspecificvar () { /* Set specific data for threads */

    pthread_setspecific (key, &struct_data);
    pthread_setspecific (key2, &temp);

    return 0;
}

Here you explicitly set both key and key2 to the same value in each thread so it shouldn't be surprising that it has the same value in each thread. Try setting it to a different value in each thread and then it will have different values in each thread.

A common pattern is this:

  1. Call pthread_getspecific . If it returns non-NULL, use that pointer.

  2. If it returns NULL, use dynamically allocate a new instance of the thread-specific object. Call pthread_setspecific to ensure the next call to pthread_getspecific from this thread returns that same object.

  3. In the pthread_key_create call, be sure to register a destructor that frees the instance of the thread-specific object when a thread goes away.

This will give each thread its own instance of a structure.

For example:

int setspecificvar () { /* Set specific data for threads */

    struct test_struct *s = malloc(sizeof(struct test_struct));
    int *i = malloc(sizeof(int *));
    memset(s, 0, sizeof(s));
    memset(i, 0, sizeof(i));

    pthread_setspecific (key, s);
    pthread_setspecific (key2, i);

    return 0;
}

This actually sets a different value in each thread. And this:

int changedata (int i, float k, int tempvar) { /* Change specific data for threads */

    struct test_struct *struct_data = pthread_getspecific(key);
    int *temp = pthread_getspecific(key2);

    *temp = tempvar;
    struct_data->i = i;
    struct_data->k = k;

    return 0;
}

This actually uses the thread-specific data.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM