Доступ и изменение проблемы данных потока в C ++ - PullRequest
1 голос
/ 13 января 2011

У меня проблемы с доступом к моим многопоточным данным и их изменением.Есть ли какой-нибудь правильный способ сделать это?

Вот мой полный код:

#include <stdio.h>
#include <windows.h>

// Create thread data structure
struct data
{
    int a;
    float b;
    char *c;
};

DWORD WINAPI threadfn(LPVOID lpParam)
{   
    printf("Address of thread data:\n");

    for(int i=0; i<sizeof(lpParam); i++)
        printf("%X\n", (int*)lpParam + i);

    // Print out initial values
    printf("\nInitial values:\n");
    printf("a: %d\n", *((int*)lpParam));
    printf("b: %.2f\n", *((float*)lpParam + 1));
    printf("c: %s\n", *((int*)lpParam + 2));

    // Modify thread data values
    *(int*)lpParam = 200;
    *((float*)lpParam + 1) = 25.80;
    *((char*)lpParam + 2) = "Es la una";

    return 0;
}

int main()
{
    HANDLE hThread;
    data thread;

    // Set initial thread data values
    thread.a = 10;                  // Integer data type
    thread.b = 15.60;               // Float data type
    thread.c = "Que hora es?";      // String data type

    hThread = CreateThread(NULL, 0, threadfn, &thread, 0, NULL);
    WaitForSingleObject(hThread, INFINITE);

    // Print out thread value after modification
    printf("\nAfter thread modifications:\n");
    printf("a: %d\n", thread.a);
    printf("b: %.2f\n", thread.b);
    printf("c: %s\n", thread.c);

    getchar();
    return 0;
}

И это мой вывод:

Address of thread data:
28FF20
28FF24
28FF28
28FF2C

Initial values:
a: 10
b: 15.60
c: Que hora es?

After thread modifications:
a: 7405768
b: 25.80
c: Que hora es?

Как видитезначение 'c' одинаково.Как изменить строковое значение?

Ответы [ 3 ]

6 голосов
/ 13 января 2011

Что ты делаешь ?! Весь кастинг lpData очень, очень неправильный. Если вам нужно выполнить такое большое количество кастов, чтобы что-то сделать, вы, вероятно, делаете это неправильно.

В любом случае, ваш код должен выглядеть так:

DWORD WINAPI threadfn(LPVOID lpParam)
{   
    printf("Address of thread data:\n");

    data *lpData = (data *)(lpParam);

    for(int i=0; i<sizeof(lpParam); i++)
        printf("%X\n", (int*)lpParam + i);

    // Print out initial values
    printf("\nInitial values:\n");
    printf("a: %d\n", lpData->a);
    printf("b: %.2f\n", lpData->b);
    printf("c: %s\n", lpData->c);

    // Modify thread data values
    lpData->a = 200;
    lpData->b = 25.80;
    lpData->c = "Es la una";

    return 0;
}

Вы должны использовать (data *)(lpParam), потому что это в основном переворачивает то, что происходит, когда вы звоните CreateThread. Лично я думаю, что глупая нотация P для имен типов является скорее помехой, чем помощью, потому что она затемняет то, что на самом деле происходит. Венгерская нотация вообще имеет эту проблему ИМХО.

В вашей функции main у вас есть этот код:

hThread = CreateThread(NULL, 0, threadfn, &thread, 0, NULL);

4-й аргумент CreateThread - это void * (он же PVOID). Тип выражения &thread: data *. Это означает, что data * неявно преобразуется в void *. Если вы сделаете это преобразование явным, код будет выглядеть так:

hThread = CreateThread(NULL, 0, threadfn, (void *)(&thread), 0, NULL);

Итак, чтобы «отменить» то, что было сделано, вам необходимо «отменить» приведение. Вам нужно превратить void * обратно в data *, что означает, что в threadfn вам нужен код data *lpData = (data *)(lpParam);.

Кроме того, вы преследуете бедствие, устанавливая c, чтобы он указывал на постоянные символьные строки, поскольку вы не объявили его как const char *. Я удивлен, что компилятор не выдает ошибку. Бедствие случается, когда вы делаете что-то вроде data.c[0] = 'f';. Когда вы это сделаете, вы будете пытаться изменить память, которая может быть помечена как доступная только для чтения и вызвать сбой вашей программы. И это самое доброе, что могло случиться.

2 голосов
/ 13 января 2011

Вы не обращаетесь к членам своей структуры должным образом из порожденной нити. Учтите это:

*(int*)lpParam = 200;

Это означает, что необходимо преобразовать lpParam в int *, а затем получить доступ к целому числу по этому адресу. Это прекрасно работает, но:

*((float*)lpParam + 1) = 25.80;

Преобразует lpParam в число с плавающей точкой *, затем добавляет к нему размер байтов (float *) и затем разыменовывает его. Это работает, только если sizeof (int) совпадает с sizeof (float) ... что достаточно распространено, но не гарантировано.

 *((char*)lpParam + 2) = "Es la una";

Это действительно беспокоит: это рассматривает lpParam как символ *, затем добавляет два байта к нему, что, вероятно, помещает его на половину в четыре байта, используемых целочисленным членом структуры (предполагая 32-битное приложение), затем записывает поверх одного символа по этому адресу усеченное значение (наименее значимый байт / символ) из указателя на новую строку [с учетом исправления благодаря комментарию Криса].

Вместо того, чтобы:

data* p = (data*)lpParam;
p->a = ...;
p->b = ...;
p->c = ...;

Суть в том, что функция потока принимает аргумент void *, поэтому вы теряете информацию о типе. Первое, что вы хотите сделать с ним, когда ваш поток начнет работать, это восстановить информацию о типе, чтобы компилятор мог проверить, что вы делаете, безопасно и разумно.

0 голосов
/ 13 января 2011

Ваша арифметика с указателем выключена.

c имеет смещение 8 в структуре.

Однако:

 *((char*)lpParam + 2) = "Es la una";

Вы приводите lpParam к символу *.У символа есть размер 1 байт (по крайней мере, в Windows). Вы добавляете два к указателю, поэтому вы пишете со смещением в 2 байта в структуре.

Ваша другая арифметика указателя работает, так как вы приводите lpParamв float *, что означает (float *), что lpParam + 1 записывает в смещение 4 в структуре.

Как предложил Omnifarius, просто приведите lpParam к указателю на структуру данных потока и через него получите доступ к членам.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...