求教高手:一个新手对多线程的几点疑惑,请不吝指点,不胜感激!

时间:2022-04-23 21:10:42
我在做一个基于winsocket的多线程的程序,具体思路如下:先在主线程启动一个派生自CAsyncSocket的对象,执行listen(),重载该对象的onaccept()函数,得到另外一个派生自CAsyncSocket的对象(以下简称connectsock),即          accept(connectsock);启动一个线程(派生自CWinThread),在该线程里有一个connectsock的对象实例,通过刚才accept(connectsock)得到的那个connectsock的句柄,使该进程管理建立连接的socket对象的数据收发工作,现问题如下:
     1.听说线程之间无法通过消息用指针(比如说字符串)来传递变量,那我在线程里收到的字符串数据无法交给主进程,但为什么我通过在线程里的connectsock对象(即在该对象的类定义里)可以把收到的字符串指针通过
afxgetmainwnd()->sendmessage()发送到主进程里?但我如何在主进程里(比如说在view类里)把要线程发送出去的数据通知线程发送?当然,用SendThreadMessage()是无法把字符串传递给线程的,但我如果通过该线程的指针
pThread->m_sock.send()又出错,为什么?又不能通过消息直接把数据发送到一个socket对象,那我该怎么做?还有,我的view通过消息机制得到线程中的connectsock对象收到的数据,但无法知道是哪个线程里的connectsock收到的数据(当然,我可以在数据里显式的指出,让主线程知道),但,这里线程到底参与管理了数据通讯吗?因为,虽然说这个connectsock的对象存在于线程中,但收发数据和传递数据到主线程里全是connectsock自己的行为?因为,照我理解,线程应该自己有通讯机制,处理事件,而我好像却不是这样的?如果我在主线程里通过什么方法(我试了好多方法,好像都不可以,所以,只是假如)直接命令该connectsock传送数据,那,就完全没有利用多线程了?
    2.如果我在线程里的connectsock通过socket句柄,从主线程得到了那个进行数据收发的connectsock的对象,那我再在主线程里通过该句柄得到这个socket的实例或指针,参与管理,是否可行?(我做下来好像是不行的)为什么?
    我想知道具体的原理,和相关的理论知识,求高手指教,也许我写的很凌乱,请见谅,最好是能电话里讨教,上海的高手请打021-62911357 找沈先生,或白天13621634395,真的感谢!!!

33 个解决方案

#1


请大家帮帮忙,这些问题已经困扰我好几天了,真的快崩溃了,或许是我本身知识的不够,请大家多多费心了,欢迎发贴讨论,更欢迎电话交流

#2


收藏!

#3


收藏?555,救命啊。。。

#4


在上海啊,这么远

#5


没有仔细看你的说明
不过问题是,根据我的经验,似乎CSocket不支持多线程。特别是
在一个线程中创建socket,另一个线程使用的时候,会出现比较严
重的问题。我当时遇到这样的问题,把CSocket类全部改成socket
 API调用就可以了。不妨试试。

#6


#define WSVERS MAKEWORD(2, 0)
#define STKSIZE 16536

#include <stdio.h>
#include <winsock2.h>
#include <process.h>

int TCP_Echo(SOCKET,struct sockaddr_in);

int main()
{
  struct sockaddr_in fsin, sin;
  SOCKET msock, ssock;
  struct hostent *hostname;
  unsigned short port = 10000;
  int alen;
  WSADATA wsadata;


  if (WSAStartup(WSVERS, &wsadata) != 0)
  {
  printf("调用winsock.dll失败!");
  return -1;
  }

  memset(&sin, 0, sizeof(sin));
  sin.sin_family = AF_INET;
  sin.sin_addr.s_addr =inet_addr("192.168.4.142"); //
  sin.sin_port = htons(port);

  msock = socket(PF_INET, SOCK_STREAM, 0);
  if (msock == INVALID_SOCKET)
  {
  printf("create socket error\n");
  return -1;
  }

  if (bind(msock, (struct sockaddr *)&sin, sizeof(sin))==SOCKET_ERROR)
  {
  printf("bind error\n");
  return -1;
  }

  if (listen(msock, 5)==SOCKET_ERROR)
  {
  printf("listen error\n");
  return -1;
  }
//hostname=gethostbyaddr(inet_ntoa(sin.sin_addr),8,0);
//printf("%s\n",hostname->h_name);
printf("本地地址:%s\n",inet_ntoa(sin.sin_addr));//
  printf("服务器监听端口:%d\n",ntohs(sin.sin_port));


  while(1)
  {
  alen = sizeof(struct sockaddr);
  ssock = accept(msock, (struct sockaddr *)&fsin, &alen);
  if (ssock == INVALID_SOCKET)
  {
  printf("accept error\n");
  return -1;
  }
  printf("有客户连接自 %s\r\n", inet_ntoa(fsin.sin_addr));
  if (_beginthread(TCP_Echo, STKSIZE, (void*)ssock)<0)
  {
  printf("启动新线程失败!\n");
  return -1;
  }
  }
}
//线程要调用的函数:处理客户发送来的数据
int TCP_Echo(SOCKET fd,struct sockaddr_in sin)
{
char buf[4096];
int  cc = 0;

memset(buf, 0, sizeof(buf));
strcpy(buf, "Enter 'Q' to exit\r\n");
if (send(fd, buf, strlen(buf), 0)==SOCKET_ERROR)
{
printf("echo send error\n");
return -1;
}

memset(buf, 0, sizeof(buf));
cc = recv(fd, buf, sizeof(buf), 0);
printf("客户发送的数据:%s\n", buf);

while(cc!=SOCKET_ERROR && cc > 0)
{
if (send(fd, buf, cc, 0)==SOCKET_ERROR)
{
printf("echo send error\n");
break;
}
/* if (send(fd, "\r\n", 2, 0)==SOCKET_ERROR)
{
printf("echo send error\n");
break;
}*/
  memset(buf, 0, sizeof(buf));
cc = recv(fd, buf, sizeof(buf), 0);
printf("客户发送的数据:%s\n", buf);
if (buf[0]=='Q')
{               

_endthread();
// return 0;
break;
}
}
if (cc==SOCKET_ERROR)
printf("echo recv error\n");

closesocket(fd);
return 0;
}

看看吧,没有用csocket。多线程的socket server,你可用telnet登陆上去!!

#7


不是不可以在线程之间通过指针传递数据,而是由于线程间访问变量是同步的,所以你只要保证其他现成访问某变量时,其处于有效生存空间就行。由于 SendMessage 是异步等待方式,所以可以。但它会阻塞发送现成,所以你要考虑清楚,否则你的多现成可能没有意义。

一般来讲只有会阻塞的操作才另外开辟现成,所有初始及结束操作均可在主现成中完成。


#8


因为 SendMessage 是阻塞的所以可以,不过此时你的多县城可能已失去意义。

除了会阻塞的操作(recv(socke...))需要另外开辟县城以外,所有初始化及结束清理工作都应该使用主县城完成。

#9


Try SendMessageCallback

#10


MFC的socket处理很烂,不能跨线程使用。用SDK的话就可以在线程之间传递句

线程之间的同步和通知可以用Event和Critical Section来做

#11


SYMPTOMS
A multi-threaded application that uses MFC's socket classes encounters a message box or debug output line that contains an error message similar to the following:

For Visual C++ 2.x: 

Assertion failed - <app name>:File sockcore.cpp, Line 837 
For Visual C++ 4.0: 
Assertion failed - <app name>:File sockcore.cpp, Line 1041 



CAUSE
Most frequently, this problem is due to the sharing of CSocket objects between multiple threads.

A CSocket object should be used only in the context of a single thread because the SOCKET handle encapsulated by a CAsyncSocket object is stored in a per-thread handle map. (CSocket is derived from CAsyncSocket.) Other information is stored on a per-thread basis, including a hidden notification window that MFC uses for socket notifications.

The assertion failure line, which can be found in Sockcore.cpp in the \Msvc20\Mfc\Src directory, is: 


   ASSERT(pThreadState->m_hSocketWindow != NULL); 
This assertion failure occurs because the CSocket object was either created or accepted in the context of another thread. The socket notification window was created in a different thread, and the m_hSocketWindow for the current thread is NULL, thus the assertion failure. 



RESOLUTION
As already mentioned, a CAsyncSocket object should be used only in the context of a single thread. If you need to switch the thread that is accessing a SOCKET connection with another thread, then you should use a separate CAsyncSocket object in each thread, and use the Detach and Attach functions to attach the CAsyncSocket object to the SOCKET handle in the thread that is about to use the socket. Use this sequence:



Use Detach() to detach the CAsyncSocket object from the SOCKET handle in the thread that is currently using the CAsyncSocket object.


Use Attach() to attach a different CAsyncSocket object to the SOCKET handle while in the context of the MFC UI thread in which you wish to begin accessing the SOCKET connection.


The code shown in the "Code Sample" section of this article shows how to handle the moment when a listening socket accepts a connection request and then begins a new thread to handle the new connection.

NOTE: One concern often arises that socket notification messages might be lost between the time the call to Detach() is made and the subsequent call to Attach() is made. This is not a concern because of the way socket notifications are handled. The implementation of CAsyncSocket::Attach() makes a call to WSAAsyncSelect to enable notifications. As mentioned in the documentation for WSAAsyncSelect, if any socket noficiations were already pending for the SOCKET, they will be re-posted.

#12


把你的mail留下,我给你一个例子。

#13


CAsyncSocket是异步机制,所以处理多线程同步是不太合适
想要不容易出错,直接用API处理,象warton({-^{moon}^-})给的例子

#14


用winsocket比较好,灵活易用

#15


gz

#16


是不是可以用一个全局变量,例如定义一个全局指针,然后初始化分配给其一部分内存空间,你的线程和主程序都知道这个全局指针,线程收到指定量的数据后发各消息给主程序,然后主程序就到这个缓冲区去取数据
或者不用全局变量也可以,将指针作为一个参数传给线程函数就可以了

#17


不会啊

#18


使用 Socket API.建立一个接收数据的环形缓冲区。在单独的线程中接受数据。线程接收到数据后,使用消息通知主线程。将缓冲区指针或者索引作为消息的参数。

#19


先保留,
再看。。。。。。。

#20


谢谢大家,首先向大家表示歉意,因为实在没有什么办法了,我的问题发了好几天都得不到回复,只好向所有在专家排行表上的专家都留了个言,如有打扰,在这里先道歉!!!
    我知道如果用winsocket api做会好一点,至少它是阻塞方式的,在线程里做个无限循环就可以搞定,但我想试试用消息机制来做,所以。。。(因为我觉得用消息机制来做,好像更符合mfc的工作机制),不过大家好像都没有回答我的问题,我更想知道这些问题的理论知识!
    其实,如果按照我这个思路,应该是能做的,至多用点笨办法:因为我线程里的socket接收数据传送到主线程已经没问题了(虽然不知其所以然),至于主线程要传送待发送的数据到线程里,可以如此做:设置一个全局CString数组,大小为500,(因为我怕前面哦数据未发送完毕,后面的数据又覆盖了前面的数据),既然SendThreadMessage()不能传送字符串到线程里,那我只要把参数设置为数组的下标,就应该可以了,不知如此设想有否问题?谢谢大家了
    不过,我希望能有高手能解答我开始提出了问题,能给出理论答案,还有,用event我觉得在工作线程里派点用场,在用户界面线程里完全可以用消息机制来代替它,不知对吗?
    我只是个新手,有很多地方可能说得不对,希望大家指正!

#21


还有,我得email是viper_sh@citiz.net,欢迎大家多多联系,共同讨论

#22


还有,我在主线程的那个监听socket的onaccept()函数里,在accept(connectsock)后,用Detach() 得到句柄,在线程里用Attach() 把句柄绑定到线程里的那个connectsock,那是否在线程里的那个connectsock得到了那个accept()的socket的实例?
那再问一个简单的问题:按照对象的生命周期来看,在onaccept()中我申明
connectsock m_sock;
accept(m_sock);
申明一个句柄hsock...
hsock=m_sock.detach();
afxbeginthread(...);
正常的话,这个m_sock应该在onaccept()结束后就销毁了,但我通过剥离句柄,再在线程里把该句柄绑定到一个socket的实例上去,该对象的生命期虽然在onaccpet()函数里结束,但该连接却继续存在?

#23


往子线程里面传socket句柄(就是SOCKET)

我这里有基于CAsyncSocket的多线程例子,不过是公司产品里面的代码,不便拿出,具体做法是使用消息队列(并非windows的PostMessage之类的那种消息)而是自己建了一个发送和一个接受队列来处理收发的消息,利用criticalsection来防止多线程破坏队列

#24


无聊,有高效的方法实现,何必另辟歧径。

#25


请教zhougaomin_007(风之力量) ,你所谓的高效的方法是指api编程吗?不是我无聊,是我想知道这些东西的原理,和我错误的原因!有时候另辟歧径可以增长不少知识

#26


线程是windows基本调度单位,当一个应用程序运行后,windows将创建一个主线程,也就是winmain函数,一个进程可以拥有多个线程,每个线程创建后都将拥有自己独立的堆空间,任何函数中定义的非static变量都是从局部堆中分配的(我指的是变量,不是指针变量指向的地址),所有全局变量都是从全局堆中分配的,所以全局变量在应用程序生命周期中地址永远不变。

线程也是一个函数,所有满足同样的约定。考虑以下的描述:
假设线程函数func,在程序运行后的装入地址为0x800000,其中定义有int local,且有全局变量int global。
当创建多个线程func时每个线程地址都是0x800000,我们考虑创建两个func线程A、B的例子。
创建后,A和B的local变量的地址是不一样的(可以通过&local来观察),而在A、B线程中看到的global的地址是一样的。现在就应该明白多线程的运行机制了吧!

下面简单描述了windows的调度过程。
当A执行到地址0x800010时,定时硬件中断产生,windows中断服务将保存好A线程的寄存器状态,同时将B的寄存器状态恢复,并设置IP寄存器开始执行B线程。

下面描述同步机制,所有同步信号都是由windows核心管理的,当某个线程等待信号时,windows中断服务检查到该线程的激活条件不满足,就不会调度该线程。所以尽量使用同步机制而不要使用自己的全局变量来控制线程运行条件。

下面再说说消息队列,每个线程都有自己独立的消息队列(好像是只要你调用过User32中的任何一个函数,windows将检查消息队列是否存在,若无将建立),所以在进程内通过消息传递数组指针是没有问题的(即使该数组是线程的内部数组,要防止线程结束将释放掉线程堆),关键注意的是数据的生命周期。为了防止数据无效,跨线程传数时尽量在全局堆中分配空间。

跨线程消息传递的问题:如果是PostMessage/PostThreadMessage那没什么,只要注意上面说的数据的生命周期。如果是SendMessage则会产生阻塞,发送消息方将挂起等待接受方处理消息完成的信号量(这个信号量是内核创建的),此时接受方并不是马上中断代码去执行,而是要等到调度起来,执行到GetMessage处才处理消息,也就是说和PostMessage比较相似(除了阻塞的差别,还有就是优先处理的差别,GetMessage将不管队列的顺序,优先抓出Send的消息)。

窗口消息理解的误区:消息不是发到窗口的,是发到线程的,只有线程中执行到DispatchMessage时才由该函数传递到窗口的回调函数。所以使用消息没有必要非创建一个窗口。

关于跨进程数据传送的问题:早期的windows(win3.1)由于不是多线程的,所以有很多消息从现在来看是跨进程传送的,比如ListBox、WM_GETTEXT等等,现在windows为兼容以前就在跨进程时作了数据的副本,并在消息处理后释放空间,所以这些消息中传递的指针是现在学习windows的人比较困惑的问题。

内存分配要注意的问题,由于分配空间方和释放空间方可能不是一个进程或是不同的DLL,所以要注意选择分配数据的方法。不同进程中传递数据要使用IMalloc,不同DLL要中避免分配和释放不在同一方的情况。


有点罗嗦了,有不对的地方请大家指正。  :)

#27


谢了,又上了一课,虽然不是很理解,但这是我学习的方向,呵呵,希望能交个朋友,viper_sh@citiz.net

#28


看看windows网络编程的第8章,在使用多线程的时候时候select模型或完成端口模型

#29


谢谢大家,虽然仍有点搞不太清,先结帖了,分数不多,大家将就

#30


user
HeapAlloc()
传递数据
微软的后台打印就是用的他

#31


来晚了。

#32


问题看来已经解决了!我是常宁!

#33


问题还没有解决,有些理论的东西仍然模糊不清,不过,很感谢那么多热心的朋友!!!

#1


请大家帮帮忙,这些问题已经困扰我好几天了,真的快崩溃了,或许是我本身知识的不够,请大家多多费心了,欢迎发贴讨论,更欢迎电话交流

#2


收藏!

#3


收藏?555,救命啊。。。

#4


在上海啊,这么远

#5


没有仔细看你的说明
不过问题是,根据我的经验,似乎CSocket不支持多线程。特别是
在一个线程中创建socket,另一个线程使用的时候,会出现比较严
重的问题。我当时遇到这样的问题,把CSocket类全部改成socket
 API调用就可以了。不妨试试。

#6


#define WSVERS MAKEWORD(2, 0)
#define STKSIZE 16536

#include <stdio.h>
#include <winsock2.h>
#include <process.h>

int TCP_Echo(SOCKET,struct sockaddr_in);

int main()
{
  struct sockaddr_in fsin, sin;
  SOCKET msock, ssock;
  struct hostent *hostname;
  unsigned short port = 10000;
  int alen;
  WSADATA wsadata;


  if (WSAStartup(WSVERS, &wsadata) != 0)
  {
  printf("调用winsock.dll失败!");
  return -1;
  }

  memset(&sin, 0, sizeof(sin));
  sin.sin_family = AF_INET;
  sin.sin_addr.s_addr =inet_addr("192.168.4.142"); //
  sin.sin_port = htons(port);

  msock = socket(PF_INET, SOCK_STREAM, 0);
  if (msock == INVALID_SOCKET)
  {
  printf("create socket error\n");
  return -1;
  }

  if (bind(msock, (struct sockaddr *)&sin, sizeof(sin))==SOCKET_ERROR)
  {
  printf("bind error\n");
  return -1;
  }

  if (listen(msock, 5)==SOCKET_ERROR)
  {
  printf("listen error\n");
  return -1;
  }
//hostname=gethostbyaddr(inet_ntoa(sin.sin_addr),8,0);
//printf("%s\n",hostname->h_name);
printf("本地地址:%s\n",inet_ntoa(sin.sin_addr));//
  printf("服务器监听端口:%d\n",ntohs(sin.sin_port));


  while(1)
  {
  alen = sizeof(struct sockaddr);
  ssock = accept(msock, (struct sockaddr *)&fsin, &alen);
  if (ssock == INVALID_SOCKET)
  {
  printf("accept error\n");
  return -1;
  }
  printf("有客户连接自 %s\r\n", inet_ntoa(fsin.sin_addr));
  if (_beginthread(TCP_Echo, STKSIZE, (void*)ssock)<0)
  {
  printf("启动新线程失败!\n");
  return -1;
  }
  }
}
//线程要调用的函数:处理客户发送来的数据
int TCP_Echo(SOCKET fd,struct sockaddr_in sin)
{
char buf[4096];
int  cc = 0;

memset(buf, 0, sizeof(buf));
strcpy(buf, "Enter 'Q' to exit\r\n");
if (send(fd, buf, strlen(buf), 0)==SOCKET_ERROR)
{
printf("echo send error\n");
return -1;
}

memset(buf, 0, sizeof(buf));
cc = recv(fd, buf, sizeof(buf), 0);
printf("客户发送的数据:%s\n", buf);

while(cc!=SOCKET_ERROR && cc > 0)
{
if (send(fd, buf, cc, 0)==SOCKET_ERROR)
{
printf("echo send error\n");
break;
}
/* if (send(fd, "\r\n", 2, 0)==SOCKET_ERROR)
{
printf("echo send error\n");
break;
}*/
  memset(buf, 0, sizeof(buf));
cc = recv(fd, buf, sizeof(buf), 0);
printf("客户发送的数据:%s\n", buf);
if (buf[0]=='Q')
{               

_endthread();
// return 0;
break;
}
}
if (cc==SOCKET_ERROR)
printf("echo recv error\n");

closesocket(fd);
return 0;
}

看看吧,没有用csocket。多线程的socket server,你可用telnet登陆上去!!

#7


不是不可以在线程之间通过指针传递数据,而是由于线程间访问变量是同步的,所以你只要保证其他现成访问某变量时,其处于有效生存空间就行。由于 SendMessage 是异步等待方式,所以可以。但它会阻塞发送现成,所以你要考虑清楚,否则你的多现成可能没有意义。

一般来讲只有会阻塞的操作才另外开辟现成,所有初始及结束操作均可在主现成中完成。


#8


因为 SendMessage 是阻塞的所以可以,不过此时你的多县城可能已失去意义。

除了会阻塞的操作(recv(socke...))需要另外开辟县城以外,所有初始化及结束清理工作都应该使用主县城完成。

#9


Try SendMessageCallback

#10


MFC的socket处理很烂,不能跨线程使用。用SDK的话就可以在线程之间传递句

线程之间的同步和通知可以用Event和Critical Section来做

#11


SYMPTOMS
A multi-threaded application that uses MFC's socket classes encounters a message box or debug output line that contains an error message similar to the following:

For Visual C++ 2.x: 

Assertion failed - <app name>:File sockcore.cpp, Line 837 
For Visual C++ 4.0: 
Assertion failed - <app name>:File sockcore.cpp, Line 1041 



CAUSE
Most frequently, this problem is due to the sharing of CSocket objects between multiple threads.

A CSocket object should be used only in the context of a single thread because the SOCKET handle encapsulated by a CAsyncSocket object is stored in a per-thread handle map. (CSocket is derived from CAsyncSocket.) Other information is stored on a per-thread basis, including a hidden notification window that MFC uses for socket notifications.

The assertion failure line, which can be found in Sockcore.cpp in the \Msvc20\Mfc\Src directory, is: 


   ASSERT(pThreadState->m_hSocketWindow != NULL); 
This assertion failure occurs because the CSocket object was either created or accepted in the context of another thread. The socket notification window was created in a different thread, and the m_hSocketWindow for the current thread is NULL, thus the assertion failure. 



RESOLUTION
As already mentioned, a CAsyncSocket object should be used only in the context of a single thread. If you need to switch the thread that is accessing a SOCKET connection with another thread, then you should use a separate CAsyncSocket object in each thread, and use the Detach and Attach functions to attach the CAsyncSocket object to the SOCKET handle in the thread that is about to use the socket. Use this sequence:



Use Detach() to detach the CAsyncSocket object from the SOCKET handle in the thread that is currently using the CAsyncSocket object.


Use Attach() to attach a different CAsyncSocket object to the SOCKET handle while in the context of the MFC UI thread in which you wish to begin accessing the SOCKET connection.


The code shown in the "Code Sample" section of this article shows how to handle the moment when a listening socket accepts a connection request and then begins a new thread to handle the new connection.

NOTE: One concern often arises that socket notification messages might be lost between the time the call to Detach() is made and the subsequent call to Attach() is made. This is not a concern because of the way socket notifications are handled. The implementation of CAsyncSocket::Attach() makes a call to WSAAsyncSelect to enable notifications. As mentioned in the documentation for WSAAsyncSelect, if any socket noficiations were already pending for the SOCKET, they will be re-posted.

#12


把你的mail留下,我给你一个例子。

#13


CAsyncSocket是异步机制,所以处理多线程同步是不太合适
想要不容易出错,直接用API处理,象warton({-^{moon}^-})给的例子

#14


用winsocket比较好,灵活易用

#15


gz

#16


是不是可以用一个全局变量,例如定义一个全局指针,然后初始化分配给其一部分内存空间,你的线程和主程序都知道这个全局指针,线程收到指定量的数据后发各消息给主程序,然后主程序就到这个缓冲区去取数据
或者不用全局变量也可以,将指针作为一个参数传给线程函数就可以了

#17


不会啊

#18


使用 Socket API.建立一个接收数据的环形缓冲区。在单独的线程中接受数据。线程接收到数据后,使用消息通知主线程。将缓冲区指针或者索引作为消息的参数。

#19


先保留,
再看。。。。。。。

#20


谢谢大家,首先向大家表示歉意,因为实在没有什么办法了,我的问题发了好几天都得不到回复,只好向所有在专家排行表上的专家都留了个言,如有打扰,在这里先道歉!!!
    我知道如果用winsocket api做会好一点,至少它是阻塞方式的,在线程里做个无限循环就可以搞定,但我想试试用消息机制来做,所以。。。(因为我觉得用消息机制来做,好像更符合mfc的工作机制),不过大家好像都没有回答我的问题,我更想知道这些问题的理论知识!
    其实,如果按照我这个思路,应该是能做的,至多用点笨办法:因为我线程里的socket接收数据传送到主线程已经没问题了(虽然不知其所以然),至于主线程要传送待发送的数据到线程里,可以如此做:设置一个全局CString数组,大小为500,(因为我怕前面哦数据未发送完毕,后面的数据又覆盖了前面的数据),既然SendThreadMessage()不能传送字符串到线程里,那我只要把参数设置为数组的下标,就应该可以了,不知如此设想有否问题?谢谢大家了
    不过,我希望能有高手能解答我开始提出了问题,能给出理论答案,还有,用event我觉得在工作线程里派点用场,在用户界面线程里完全可以用消息机制来代替它,不知对吗?
    我只是个新手,有很多地方可能说得不对,希望大家指正!

#21


还有,我得email是viper_sh@citiz.net,欢迎大家多多联系,共同讨论

#22


还有,我在主线程的那个监听socket的onaccept()函数里,在accept(connectsock)后,用Detach() 得到句柄,在线程里用Attach() 把句柄绑定到线程里的那个connectsock,那是否在线程里的那个connectsock得到了那个accept()的socket的实例?
那再问一个简单的问题:按照对象的生命周期来看,在onaccept()中我申明
connectsock m_sock;
accept(m_sock);
申明一个句柄hsock...
hsock=m_sock.detach();
afxbeginthread(...);
正常的话,这个m_sock应该在onaccept()结束后就销毁了,但我通过剥离句柄,再在线程里把该句柄绑定到一个socket的实例上去,该对象的生命期虽然在onaccpet()函数里结束,但该连接却继续存在?

#23


往子线程里面传socket句柄(就是SOCKET)

我这里有基于CAsyncSocket的多线程例子,不过是公司产品里面的代码,不便拿出,具体做法是使用消息队列(并非windows的PostMessage之类的那种消息)而是自己建了一个发送和一个接受队列来处理收发的消息,利用criticalsection来防止多线程破坏队列

#24


无聊,有高效的方法实现,何必另辟歧径。

#25


请教zhougaomin_007(风之力量) ,你所谓的高效的方法是指api编程吗?不是我无聊,是我想知道这些东西的原理,和我错误的原因!有时候另辟歧径可以增长不少知识

#26


线程是windows基本调度单位,当一个应用程序运行后,windows将创建一个主线程,也就是winmain函数,一个进程可以拥有多个线程,每个线程创建后都将拥有自己独立的堆空间,任何函数中定义的非static变量都是从局部堆中分配的(我指的是变量,不是指针变量指向的地址),所有全局变量都是从全局堆中分配的,所以全局变量在应用程序生命周期中地址永远不变。

线程也是一个函数,所有满足同样的约定。考虑以下的描述:
假设线程函数func,在程序运行后的装入地址为0x800000,其中定义有int local,且有全局变量int global。
当创建多个线程func时每个线程地址都是0x800000,我们考虑创建两个func线程A、B的例子。
创建后,A和B的local变量的地址是不一样的(可以通过&local来观察),而在A、B线程中看到的global的地址是一样的。现在就应该明白多线程的运行机制了吧!

下面简单描述了windows的调度过程。
当A执行到地址0x800010时,定时硬件中断产生,windows中断服务将保存好A线程的寄存器状态,同时将B的寄存器状态恢复,并设置IP寄存器开始执行B线程。

下面描述同步机制,所有同步信号都是由windows核心管理的,当某个线程等待信号时,windows中断服务检查到该线程的激活条件不满足,就不会调度该线程。所以尽量使用同步机制而不要使用自己的全局变量来控制线程运行条件。

下面再说说消息队列,每个线程都有自己独立的消息队列(好像是只要你调用过User32中的任何一个函数,windows将检查消息队列是否存在,若无将建立),所以在进程内通过消息传递数组指针是没有问题的(即使该数组是线程的内部数组,要防止线程结束将释放掉线程堆),关键注意的是数据的生命周期。为了防止数据无效,跨线程传数时尽量在全局堆中分配空间。

跨线程消息传递的问题:如果是PostMessage/PostThreadMessage那没什么,只要注意上面说的数据的生命周期。如果是SendMessage则会产生阻塞,发送消息方将挂起等待接受方处理消息完成的信号量(这个信号量是内核创建的),此时接受方并不是马上中断代码去执行,而是要等到调度起来,执行到GetMessage处才处理消息,也就是说和PostMessage比较相似(除了阻塞的差别,还有就是优先处理的差别,GetMessage将不管队列的顺序,优先抓出Send的消息)。

窗口消息理解的误区:消息不是发到窗口的,是发到线程的,只有线程中执行到DispatchMessage时才由该函数传递到窗口的回调函数。所以使用消息没有必要非创建一个窗口。

关于跨进程数据传送的问题:早期的windows(win3.1)由于不是多线程的,所以有很多消息从现在来看是跨进程传送的,比如ListBox、WM_GETTEXT等等,现在windows为兼容以前就在跨进程时作了数据的副本,并在消息处理后释放空间,所以这些消息中传递的指针是现在学习windows的人比较困惑的问题。

内存分配要注意的问题,由于分配空间方和释放空间方可能不是一个进程或是不同的DLL,所以要注意选择分配数据的方法。不同进程中传递数据要使用IMalloc,不同DLL要中避免分配和释放不在同一方的情况。


有点罗嗦了,有不对的地方请大家指正。  :)

#27


谢了,又上了一课,虽然不是很理解,但这是我学习的方向,呵呵,希望能交个朋友,viper_sh@citiz.net

#28


看看windows网络编程的第8章,在使用多线程的时候时候select模型或完成端口模型

#29


谢谢大家,虽然仍有点搞不太清,先结帖了,分数不多,大家将就

#30


user
HeapAlloc()
传递数据
微软的后台打印就是用的他

#31


来晚了。

#32


问题看来已经解决了!我是常宁!

#33


问题还没有解决,有些理论的东西仍然模糊不清,不过,很感谢那么多热心的朋友!!!