我在访问和修改多线程数据时遇到问题。有没有正确的方法来做到这一点?
这是我的完整代码:
#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'值相同。我如何修改字符串值?
答案 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强制转换为指向线程数据结构的指针,然后通过它访问成员。