3

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?

Nick Dong
  • 3,638
  • 8
  • 47
  • 84
  • 1
    At least for `gcc` why not use the storage class keyword `__thread` to declare a variable as thread local storage? – alk Jan 11 '13 at 19:26

1 Answers1

5
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.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • I have call changedata() to change the value of struct_data and temp in each thread. @David Schwartz – Nick Dong Jan 11 '13 at 14:08
  • Did you say binding different variable to keys in different thread? – Nick Dong Jan 11 '13 at 14:14
  • Can you give me an example? I really confused about this problem. thanks for your time. @David Schwartz – Nick Dong Jan 11 '13 at 14:18
  • @NickDong: `changedata` changes the contents of `temp` and `struct_data`, but they are global variables shared by all threads, not thread-specific data. The only thread-specific data you have stores the *addresses* of these global structures, which you set to the same value in every thread. Use the pattern in my answer. – David Schwartz Jan 11 '13 at 18:55
  • Now, I understand what did you say. Buy why did you call memset() after calling malloc(). Malloc() have allocated memory to variables, right? I reprogram the code, but I still got Segmentation fault (core dumped) . I submit my code at [http://codeviewer.org/view/code:2e3b](http://codeviewer.org/view/code:2e3b) . Can you help me again? @David Schwartz – Nick Dong Jan 12 '13 at 10:10
  • @NickDong: Your `setspecificvar` function is broken. It sets the thread specific data to the stack addresses of `td` and `temp` rather than their *values*, which are pointers to the memory you allocated. (Notice the `&` in your calls to `pthread_setspecific` that my calls don't have.) – David Schwartz Jan 12 '13 at 10:13
  • I have defined data destructor function for each key. Should I only define one data destructor for all keys? @David Schwartz – Nick Dong Jan 12 '13 at 10:13
  • @NickDong: Either way is fine, since they do the same thing. – David Schwartz Jan 12 '13 at 10:14
  • yes, thanks, David Schwartz. now I know what is thread specific data and how to use it. – Nick Dong Jan 12 '13 at 11:52