在c ++中访问和修改线程数据问题

时间:2011-01-13 06:46:40

标签: c++ windows multithreading

我在访问和修改多线程数据时遇到问题。有没有正确的方法来做到这一点?

这是我的完整代码:

#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 个答案:

答案 0 :(得分:6)

你到底在做什么?!所有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符号更多地是一种阻碍而不是帮助,因为它模糊了实际发生的事情。 Hungarian notation一般都有这个问题恕我直言。

main函数中,您有以下代码:

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

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';之类的操作时,就会发生灾难。当你这样做时,你将尝试修改可能被标记为只读的内存,并导致程序崩溃。这是可能发生的最好的事情。

答案 1 :(得分:2)

您无法从生成的线程中正确访问您的结构成员。考虑一下:

*(int*)lpParam = 200;

这意味着将lpParam转换为int *,然后访问该地址的整数。这很好,但是:

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

将lpParam转换为float *,然后将sizeof(float *)字节添加到它,然后取消引用它。只有当sizeof(int)恰好与sizeof(float)相同时才有效...这很常见但不能保证。

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

这是一个真正的担心:这会将lpParam视为char *,然后向其添加两个BYTES,这可能会将其定位到结构的整数成员使用的四个字节的一半(假设是32位应用程序),然后在该地址的单个字符上写入一个截断值(最低有效字节/字符),从char指针指向新字符串[由于Chris的评论而包含更正]。

相反:

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

这里的基本点是线程函数采用void *参数,因此您丢失了类型信息。当你的线程开始运行时,你要做的第一件事就是恢复那个类型信息,这样编译器可以检查你正在做的事情是安全和合理的。

答案 2 :(得分:0)

指针算术已关闭。

c在结构中的偏移量为8。

然而:

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

您将lpParam强制转换为char *。 Char的大小为1个字节(至少在Windows上。)您向指针添加两个,因此您在结构中写入偏移量为2个字节。

你的其他指针算法恰好工作,因为你将lpParam转换为float *,意思是(float *)lpParam + 1写入结构中的偏移量4。

正如Omnifarius建议的那样,只需将lpParam强制转换为指向线程数据结构的指针,然后通过它访问成员。