木头 发表于 2009-8-16 10:59:33

【求助】创建多线程的问题

<p><font face="Verdana"><a href="http://www.m5home.com/blog/article.asp?id=54" target="_blank">http://www.m5home.com/blog/article.asp?id=54</a></font></p>
<p>这个多线程程序,发现如果要创建多个线程的话,如果在过程里创建就winsock的send方法就不能使用,但如果在load事件里创建就没问题,不过如果在load事件里创建的话在任务管理器的线程数里没发现多的线程,这还是不是多线程..........</p>

木头 发表于 2009-8-22 12:16:11

老马说的对哦,太谢谢老马了

马大哈 发表于 2009-8-17 02:30:05

<p>winsock控件本身,内部是多线程实现的.</p>
<p>&nbsp;</p>
<p>只要你处理数据的过程不会引起工程挂起就行了.</p>
<p>&nbsp;</p>
<p>而且这控件不清楚是否线程安全(貌似不少VB6的控件都不是线程安全的,贸然使用有可能会有隐患).</p>
<p>&nbsp;</p>
<p>对于这种多线程方案,个人建议使用在分离界面与控制上....</p>

木头 发表于 2009-8-18 23:39:35

<p>可是如果我在一般的工程里使用控件数组处理的话,有时会接收不到数据,也没发现是多线程的</p>
<p>那如果用你的方法采用双线程处理的话是不是就不会出现上面的情况</p>

马大哈 发表于 2009-8-19 20:06:04

<p>我建议,只是利用另一线程来做"处理数据"的工作.</p>
<p>&nbsp;</p>
<p>因为处理数据往往会比较费时,而如果与WINSOCK处于同一线程,则有可能影响数据的接收.</p>

jessezappy 发表于 2009-11-3 17:29:22

<p>感谢老马答疑,我简单说下我目前程序实现的功能。</p>
<p>启动后使用sock进行侦听,有客户端连接的话用另一个sock数组中闲置的元素来接受客户端登陆请求。之后客户端会发送一条有ID+CMD的命令过来,解析ID和CMD后,启动我事先加载Timer控件数组中的一个元素来执行后续的数据发送工作。这个Timer控件数组元素中需要实现的功能为,根据刚才解析的ID,和CMD内容,决定是否从SQL数据库中查询该ID对应的信息,需要查询时,根据条件,将该ID在数据库中标记为未发送的信息取出,然后用对应的SOCK数组元素将这条消息 Senddata 出去。之后这个Timer控件数组元素的工作就完成了。置<font face="Verdana">.Enabled = False</font>。现在在接受数据的过程中,由于处理数据量不大,不会阻塞,但是在发送数据时,由于客户端是手机模块改造的,用的是GPRS网络,我在局域网内模拟几千客户端连接这个服务程序也不会存在这种阻塞问题,所以我怀疑是由于 Senddata 方法造成了阻塞,因为其他的查询数据库再分析的过程耗时并不多。现在程序运行一切正常,问题就是这个 阻塞的问题导致 Timer 空间数组元素响应迟缓,严重的话,某些Timer控件数组元素响应基本停顿。因此想看看能不能用 “<font face="Verdana">在VB6里面实现稳定的多线程</font>”里面的办法来将各个Timer分去单独的线程进行处理。我正在仔细研究“<font face="Verdana">在VB6里面实现稳定的多线程</font>”这个例子。</p>
<p>又仔细翻了一下老马的其他例子发现,我虽然用了7~8年VB6了但是编程思路和能力还停留以前学的TrueBasic的过程设计上,对类模块可以说是完全没有实际编写过,都是拿别人的来用,还看不大懂。看来我的VB还没真正入门啊,以后要学的还有很多很多。。。。。。</p>

jessezappy 发表于 2009-11-3 23:28:35

<font face="Verdana">我现在头疼的问题第一个是如何建立多个类似控件数组之类的线程可以让我方便的定位各个线程进行操作。总不能手工写一堆 set A1=...&nbsp; Set A1000= ... 之类的变量出来吧。<br/>下一个头疼的问题是不晓得 winsock 在线程里面能不能接受主线程winsock 侦听时提交的 (requestID)&nbsp; 来 Accept .</font>

马大哈 发表于 2009-4-11 10:04:15

<p>这里有个技巧,就是定义对象数组,再把处理代码放在对象里面。</p>
<p>&nbsp;</p>
<p>不过,这样的话消耗内存会比较多,因为对象多了。</p>
<p>&nbsp;</p>
<p>VB6的单元线程仅比多进程少一些消耗而已,可见开销还是非常大的。。。。。</p>
<p>&nbsp;</p>
<p>具体做法:</p>
<p>&nbsp;</p>
<p>一,定义一个窗体,里面放上WINSOCK数组及一个定时器(用于返回控制权),主要工作就在这里完成。</p>
<p>&nbsp;</p>
<p>二,定义一个类,里面做好与上面窗体的通讯工作,以及通知主线程的方式(建议使用PostMessage向主窗体的句柄投放消息,主窗体收到后再根据参数向相应的对象里取信息),这个类的实例以及上面一个窗体的实例将在一个单独的线程里工作。</p>
<p>&nbsp;</p>
<p>三,主窗体里定义好上面这个类的数组,再使用CreateObject初始化,这样每个对象都在一个线程内了。</p>
<p>&nbsp;</p>
<p>四,主窗体里需要做子类化来接收类里面的消息。为什么不直接由类调用主窗体的方法来完成,是不想有太多的开销,因为这种线程间的调度开销太大,能少调就尽量少调。。。。。。</p>
<p>&nbsp;</p>
<p>具体代码形式可参考这个代码:</p>
<p>&nbsp;</p>
<p><font face="Verdana"><a href="http://www.m5home.com/blog/article.asp?id=287">http://www.m5home.com/blog/article.asp?id=287</a></font></p>

jessezappy 发表于 2009-11-5 01:09:59

<p>再次感谢老马的详细思路,我看了一下,上面提到的代码就是那个双核CPU物理多线程的例子啊,之前我在CSDN下载了看了一下,</p>
<p>根据这个思路,我先做个几千连接的例子试试。。。另外,定义对象数组,我还没搞过,还得查查资料去先。</p>
<p>整个思路和我猜测的差不多,我也是担心线程多了开销会增加。而且我还没整明白和主线程通讯的办法,还得研究一下那几个例子。</p>
<p>今天单位天没亮就停电,现在才来了回来看看,只能明天再搞了。。。</p>

马大哈 发表于 2009-11-5 10:37:29

<p>好,支持折腾,呵呵........</p>
<p>&nbsp;</p>
<p>现在VB6里面使用单元线程的人不多,好好地研究一下将它的经验多多交流一下吧.</p>

jessezappy 发表于 2009-11-2 22:54:02

<div class="msgheader">QUOTE:</div><div class="msgborder"><b>以下是引用<i>马大哈</i>在2009-8-19 20:06:04的发言:</b><br/>
<p>我建议,只是利用另一线程来做"处理数据"的工作.</p>
<p>&nbsp;</p>
<p>因为处理数据往往会比较费时,而如果与WINSOCK处于同一线程,则有可能影响数据的接收.</p></div>
<p>请教老马,我现在的问题是 WINSOCK 接收到客户端消息后,根据消息内容(ID+CMD)简单判断后,启动一个时钟控件数组元素对应 WINSOCK 的index,由这个时钟控件里面的代码根据ID去查询数据库,有该ID的记录就将记录内容 <font face="Verdana">Sock(Index).SendData OutTxt</font>&nbsp; 给客户端。而经过实际使用和我单独测试发现,因为 Timer控件数组不是多线程的,因此,在登录ID较多的情况下,处理 <font face="Verdana">Sock(Index).SendData OutTxt</font> 的时钟会发生阻塞,本来设置好 收到数据分析后 3秒启动这个 Timer控件数组元素的,可是由于阻塞,有的 Timer控件数组元素会在20几秒后才开始响应。</p>
<p>那么这种问题用那个ActiveX多线程好像不好解决啊。因为我接受客户端连接也是用的 WinSock数组来进行的。有没有 Winsock控件数组+多线程发送数据给客户端的好思路呢?</p>

马大哈 发表于 2009-11-3 01:44:49

<p>你这个就不是"实现多线程"的问题了,完全是"应用多线程"的问题....</p>
<p>&nbsp;</p>
<p>大概想了一下,你的工程里定时器应该是巨多,而定时器本身的机制是SetTimer函数来完成的,不是多线程,因此也只有顺序执行.</p>
<p>&nbsp;</p>
<p>同一时间必然只有一个定时器产生Timer事件,再加上处理代码的工作时间,其余的定时器肯定得延后工作.</p>
<p>&nbsp;</p>
<p>对于你这个问题我也想不到什么好的结构来处理,只能想到一个治标的办法,就是做个逻辑"池",让单位线程处理的WINSOCK数量限制在一定值里面.</p>
<p>&nbsp;</p>
<p>比如,一个线程处理5个WINSOCK.</p>
<p>&nbsp;</p>
<p>这样的话多个用户时就有多个线程在同时工作.</p>
<p>&nbsp;</p>
<p>不过....这种情况下,多个线程之间的协调又是问题了------假如共享数据的读写上面还是需要使用排他锁一类机制的话,还是等于串行执行化了,用上多线程也不会有更好的效率发挥.</p>
<p>&nbsp;</p>
<p>挺复杂.......</p>

jessezappy 发表于 2009-11-7 14:50:11

<p>我前两天先将那个工程改了改,将原来的由每个Sock对应一个Timer改成整个工程只使用一个Timer了,因为经过这几天的测试发现,Timer控件数组不能实现并行运行(之前是我一厢情愿没仔细分析),也证明了程序正常运行时Timer在内部代码操作时间大于其激活时间时是不会发生重入的,而如果 Unload me 后出现了对窗口上任何控件的调用导致已经关闭的Timer控件又活过来的话,致使整个程序不能正常关闭(因为主窗口显示不出来了,而程序进程依然存在于内存),在运行一段时间后,Timer将发生“重入”,当然这属于一个编程的逻辑错误,所以这种重入是没有意义的,只要程序正常结束就不存在这个问题了。</p>
<p>更改后,将由这一个Timer的过程对所有Sock进行轮询,有需要操作的就操作,没有的跳过,等下一轮轮询,这样就不会因为阻塞而延迟任何一个Sock的发送操作了,每个Sock都有机会得到一次操作时间,相当于排队,当然这个排队过程可能会稍微有点长,但是和以前使用Timer控件数组来做的效率是一样的,这样的好处是不会因为不排队挤压导致有的Sock怎么也挤不到前头。</p>
<p>这个也只能是权益之计,我先将编译程序交给朋友拿去试用,现在才转头过来研究多线程的运用,感觉多线程才是解决那个阻塞问题的最终方案。</p>
<p>经过这两天的资料搜索和简单实验,发现,不能建立对象数组事件:</p>
<p><font face="Verdana">Private WithEvents myObjects() As SomeClass</font> '这样的写法是错误的。</p>
<p>只能建立对象数组,其事件不能搞成数组。如果事件不能用数组操作的话,就难以控制所有建立的对象线程,也就不能动态增删对象线程了。</p>
<p>今天找到个另一个例子: <font face="Verdana"><a href="http://www.cnblogs.com/raylynn/archive/2007/01/29/632978.html">http://www.cnblogs.com/raylynn/archive/2007/01/29/632978.html</a></font> ,里面好像有实现对象数组事件自定义的方法,正在研究中。。。。。。</p>

马大哈 发表于 2009-11-7 15:53:54

<p><font face="Verdana">你那个方法实现数组对象事件处理是可以,但是...........用在多线程里时,这样就是线程间调度了,而这种线程间调度在VB6里面是非常大的开销的..........</font></p>
<p><font face="Verdana"></font>&nbsp;</p>
<p><font face="Verdana">我看你还是使用我上面10楼提到的方法吧.</font></p>
<p><font face="Verdana"></font>&nbsp;</p>
<p><font face="Verdana">那样的话事件虽然不能集中到主窗体中处理,但程序结构我觉得要好些,而且不存在线程间调度频繁的问题.....</font></p>
<p><font face="Verdana"></font>&nbsp;</p>
<p><font face="Verdana">另外,要注意的是,每个线程里包含的对象至少有两个,即一个线程类的实例,以及包含WINSOCK控件的窗体的实例.</font></p>
<p><font face="Verdana"></font>&nbsp;</p>
<p><font face="Verdana">不能直接将某个窗体的WINSOCK控件拿来用,默认实例只有一个,要自己NEW新的实例....</font></p>

jessezappy 发表于 2009-11-7 18:16:46

<p>我拿&nbsp;<font face="Verdana"><a href="http://www.cnblogs.com/raylynn/archive/2007/01/29/632978.html" target="_blank">http://www.cnblogs.com/raylynn/archive/2007/01/29/632978.html</a></font>&nbsp;和老马你那个多线程下载例子进行和整,结果感觉&nbsp;&nbsp;<font face="Verdana"><a href="http://www.cnblogs.com/raylynn/archive/2007/01/29/632978.html" target="_blank">http://www.cnblogs.com/raylynn/archive/2007/01/29/632978.html</a></font>&nbsp;里面的例子没办法拿过来运用在多线程上,而且那个例子里面也是用事件调度,既然要用Post消息的话,那么这个就没有意义了。</p>
<p>于是。。暂时测试了一下简化那个 多线程下载例子:</p>
<p>我将多线程下载中多余的代码删了,在线程窗口中增加了一个Timer,将AddJob改成 <font face="Verdana">Timer1.Enabled = True </font></p>
<p><font face="Verdana">同时设置一个Busy标志 <font face="Verdana">RaiseEvent ImBusy ,主线程通过 类的 <font face="Verdana">.busy 属性来访问这个标志。</font></font></font></p>
<p>线程里面的 Timer 运行10秒后通过 <font face="Verdana">RaiseEvent</font>&nbsp; 设置 Busy 为 False 。</p>
<p>这样应该也算是一种线程将通讯方式吧,只是实验。Post 消息的办法我还在查找资料中。。以前基本没玩过自定义消息控制。</p>
<p>因为我的设想是,主线程 接收到客户端登录消息后,将轮询 所有子线程的Busy标志,闲置的就调用事先设置的 Accept 方法让它去接收新的ID连接,之后主线程就不再需要理会子线程的运作情况了,只要掌握它的Busy标识即可。其他的工作在子线程里面自己去完成了。</p>
<p>我编译运行,发现如果在主窗口Form_Load事件里面 </p>
<p><font face="Verdana">ReDim myWidget(5)</font></p>
<p><font face="Verdana">Set myWidget(i) = CreateObject("MyMth.Pcls") 的话,程序运行后在任务管理器里面只能看到一个线程。我应该是建立了6个线程的。</font></p>
<p>试了几次后无奈,想试下动态增加线程,于是将上面 CreateObject 移到一个按钮事件里面去操作。这下无意中发现主窗口出来后,点击那个 CreateObject 的按钮,线程数忽然就增加了6个。原先我还以为是由于使用对象数组的话,一个数组只有一个线程呢。这个问题就有点奇怪了,多线程不能在 <font face="Verdana">Form_Load</font> 里面创建。。。。。。。奇怪啊。。。。。</p>
<p>下面接着研究用 Post 消息来进行通讯。。。。。。</p>

jessezappy 发表于 2009-11-8 11:47:40

查询了一堆资料发现,<font face="Verdana">PostMessage </font>只能传递消息代码,如果要传递一个字符串应该就不行了啊。郁闷啊。

jessezappy 发表于 2009-11-8 11:49:32

<p>忘记上面10楼写的这句了:</p>
<p>主窗体收到后再根据参数向相应的对象里取信息)</p>
<p>看来还有希望。继续研究。</p>

马大哈 发表于 2009-11-8 12:36:39

<div class="msgheader">QUOTE:</div><div class="msgborder"><b>以下是引用<i>jessezappy</i>在2009-11-8 11:47:40的发言:</b><br/>查询了一堆资料发现,<font face="Verdana">PostMessage </font>只能传递消息代码,如果要传递一个字符串应该就不行了啊。郁闷啊。 </div>
<p>Post因为是"Post",消息是投递到目标句柄的消息队列的,所以在这个函数的参数里包含指针的话,并不能确保目标句柄的消息循环在处理到这个消息时,那个指针还是有效的.</p>
<p>&nbsp;</p>
<p>VB的字符串其实也是一个指针,虽然你可以强行地将字符串的地址用StrPtr取到后发到过去,但目标句柄的消息循环处理时,如果那个地址已经无效了(比如是一个临时变量),那就会引起不可预料的后果,运气好的话,是读出错误的内容了,严重的话就会看到熟悉的"XXXXXX内存不可读"的错误.....</p>
<p>&nbsp;</p>
<p>所以微软干脆禁止在这个函数的参数里传送指针,这也是情理之中了,因为Post的方式,并非"马上处理".</p>
<p>&nbsp;</p>
<p>而在现在这个需求中,如果使用SendMessage,也不行,因为SendMessage虽然是马上处理的,但是在处理的过程中,实际上函数是未返回的,调用这个函数的线程是被挂起的,直到这个函数返回.</p>
<p>&nbsp;</p>
<p>这样的话多线程又等于变成串行处理了.............</p>

马大哈 发表于 2009-11-8 12:46:27

<p>现在这种情况,我觉得比较好的方式是,数据到来时,POST一个消息,在参数里说明一下当前线程的一个唯一身份信息,比如线程ID(<font face="Verdana">App.ThreadID返回当前线程ID),然后等待主线程根据这些信息找到自己,并取走数据.</font></p>
<p>&nbsp;</p>
<p>这对于更新界面的情况,是很有效的,因为这些数据不需要主线程即时处理.</p>
<p>&nbsp;</p>
<p>如果需要即时处理的,就只有直接调用主线程对象的方法了,因为反正要挂起,用API不如直接线程间调度来得简便.....只是需要看看开销带来的影响是否太大,不然还是得用API.....</p>

马大哈 发表于 2009-11-8 13:26:40

<div class="msgheader">QUOTE:</div><div class="msgborder"><b>以下是引用<i>jessezappy</i>在2009-11-8 11:47:40的发言:</b><br/>查询了一堆资料发现,<font face="Verdana">PostMessage </font>只能传递消息代码,如果要传递一个字符串应该就不行了啊。郁闷啊。 </div>
<p>Post因为是"Post",消息是投递到目标句柄的消息队列的,所以在这个函数的参数里包含指针的话,并不能确保目标句柄的消息循环在处理到这个消息时,那个指针还是有效的.</p>
<p>&nbsp;</p>
<p>VB的字符串其实也是一个指针,虽然你可以强行地将字符串的地址用StrPtr取到后发到过去,但目标句柄的消息循环处理时,如果那个地址已经无效了(比如是一个临时变量),那就会引起不可预料的后果,运气好的话,是读出错误的内容了,严重的话就会看到熟悉的"XXXXXX内存不可读"的错误.....</p>
<p>&nbsp;</p>
<p>所以微软干脆禁止在这个函数的参数里传送指针,这也是情理之中了,因为Post的方式,并非"马上处理".</p>
<p>&nbsp;</p>
<p>而在现在这个需求中,如果使用SendMessage,也不行,因为SendMessage虽然是马上处理的,但是在处理的过程中,实际上函数是未返回的,调用这个函数的线程是被挂起的,直到这个函数返回.</p>
<p>&nbsp;</p>
<p>这样的话多线程又等于变成串行处理了.............</p>

马大哈 发表于 2009-11-8 13:49:41

<div class="msgheader">QUOTE:</div><div class="msgborder"><b>以下是引用<i>jessezappy</i>在2009-11-8 11:47:40的发言:</b><br/>查询了一堆资料发现,<font face="Verdana">PostMessage </font>只能传递消息代码,如果要传递一个字符串应该就不行了啊。郁闷啊。 </div>
<p>Post因为是"Post",消息是投递到目标句柄的消息队列的,所以在这个函数的参数里包含指针的话,并不能确保目标句柄的消息循环在处理到这个消息时,那个指针还是有效的.</p>
<p>&nbsp;</p>
<p>VB的字符串其实也是一个指针,虽然你可以强行地将字符串的地址用StrPtr取到后发到过去,但目标句柄的消息循环处理时,如果那个地址已经无效了(比如是一个临时变量),那就会引起不可预料的后果,运气好的话,是读出错误的内容了,严重的话就会看到熟悉的"XXXXXX内存不可读"的错误.....</p>
<p>&nbsp;</p>
<p>所以微软干脆禁止在这个函数的参数里传送指针,这也是情理之中了,因为Post的方式,并非"马上处理".</p>
<p>&nbsp;</p>
<p>而在现在这个需求中,如果使用SendMessage,也不行,因为SendMessage虽然是马上处理的,但是在处理的过程中,实际上函数是未返回的,调用这个函数的线程是被挂起的,直到这个函数返回.</p>
<p>&nbsp;</p>
<p>这样的话多线程又等于变成串行处理了.............</p>

马大哈 发表于 2009-11-8 13:50:50

<div class="msgheader">QUOTE:</div><div class="msgborder"><b>以下是引用<i>jessezappy</i>在2009-11-8 11:47:40的发言:</b><br/>查询了一堆资料发现,<font face="Verdana">PostMessage </font>只能传递消息代码,如果要传递一个字符串应该就不行了啊。郁闷啊。 </div>
<p>Post因为是"Post",消息是投递到目标句柄的消息队列的,所以在这个函数的参数里包含指针的话,并不能确保目标句柄的消息循环在处理到这个消息时,那个指针还是有效的.</p>
<p>&nbsp;</p>
<p>VB的字符串其实也是一个指针,虽然你可以强行地将字符串的地址用StrPtr取到后发到过去,但目标句柄的消息循环处理时,如果那个地址已经无效了(比如是一个临时变量),那就会引起不可预料的后果,运气好的话,是读出错误的内容了,严重的话就会看到熟悉的"XXXXXX内存不可读"的错误.....</p>
<p>&nbsp;</p>
<p>所以微软干脆禁止在这个函数的参数里传送指针,这也是情理之中了,因为Post的方式,并非"马上处理".</p>
<p>&nbsp;</p>
<p>而在现在这个需求中,如果使用SendMessage,也不行,因为SendMessage虽然是马上处理的,但是在处理的过程中,实际上函数是未返回的,调用这个函数的线程是被挂起的,直到这个函数返回.</p>
<p>&nbsp;</p>
<p>这样的话多线程又等于变成串行处理了.............</p>

jessezappy 发表于 2009-11-8 15:20:06

<div class="msgheader">QUOTE:</div><div class="msgborder"><b>以下是引用<i>马大哈</i>在2009-11-8 12:46:27的发言:</b><br/>
<p>现在这种情况,我觉得比较好的方式是,数据到来时,POST一个消息,在参数里说明一下当前线程的一个唯一身份信息,比如线程ID(<font face="Verdana">App.ThreadID返回当前线程ID),然后等待主线程根据这些信息找到自己,并取走数据.</font></p>
<p>&nbsp;</p>
<p>这对于更新界面的情况,是很有效的,因为这些数据不需要主线程即时处理.</p>
<p>&nbsp;</p>
<p>如果需要即时处理的,就只有直接调用主线程对象的方法了,因为反正要挂起,用API不如直接线程间调度来得简便.....只是需要看看开销带来的影响是否太大,不然还是得用API.....</p></div>
<p>&nbsp;</p>
<p>消息投递我根据10楼写的这句: 主窗体收到后再根据参数向相应的对象里取信息,做出来了。但是遇到了一个郁闷的情况,我称它为“消息阻塞”。</p>
<p>'-------------------------------</p>
<p>我用了以前的键盘钩子的方式,找了个消息挂钩的例子改了一下。做了个消息钩子,</p>
<p>’------------消息钩子模块。</p>
<p><font face="Verdana">Option Explicit</font></p>
<p><font face="Verdana">Private Const GWL_WNDPROC = -4<br/>Private Const GWL_USERDATA = (-21)<br/>Private Const WM_SIZE = &amp;H5<br/>Private Const WM_USER = &amp;H400<br/>&nbsp;&nbsp;&nbsp; <br/>Private Declare Function CallWindowProc Lib "user32" Alias _<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "CallWindowProcA" (ByVal lpPrevWndFunc As Long, _<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ByVal hwnd As Long, ByVal Msg As Long, _<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ByVal wParam As Long, ByVal lParam As Long) As Long<br/>&nbsp;&nbsp;&nbsp; <br/>Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (ByVal hwnd As Long, ByVal nIndex As Long) As Long<br/>&nbsp; <br/>Private Declare Function SetWindowLong Lib "user32" Alias _<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "SetWindowLongA" (ByVal hwnd As Long, _<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ByVal nIndex As Long, ByVal dwNewLong As Long) As Long<br/>'---------------------<br/>Private prevWndProc As Long<br/>&nbsp;&nbsp;&nbsp; <br/>Public Function Hook(ByVal hwnd As Long) As Long<br/>&nbsp;&nbsp;&nbsp; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim pOld&nbsp;&nbsp;&nbsp;&nbsp; As Long<br/>'&nbsp; ‘指定自定义的窗口过程<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pOld = SetWindowLong(hwnd, GWL_WNDPROC, AddressOf WindowProc)<br/>'&nbsp; ‘保存原来默认的窗口过程指针<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Hook = pOld<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; prevWndProc = pOld<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Debug.Print "原来默认的窗口过程指针Pold:" &amp; pOld<br/>End Function<br/>&nbsp;&nbsp;&nbsp; <br/>Public Sub Unhook(ByVal hwnd As Long, ByVal lpWndProc As Long)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim temp&nbsp;&nbsp;&nbsp;&nbsp; As Long<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 'Cease&nbsp;&nbsp; subclassing.<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; temp = SetWindowLong(hwnd, GWL_WNDPROC, lpWndProc)<br/>End Sub<br/>&nbsp;&nbsp;&nbsp; <br/>Function WindowProc(ByVal hw As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long<br/>&nbsp;&nbsp;&nbsp; Select Case uMsg<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Case WM_SIZE<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; '&nbsp; ‘处理WM_SIZE消息<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Debug.Print "SIZE"<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Case WM_USER + 1<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Select Case wParam<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Case 1<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; frmMain.Text1(lParam) = mySock_mTh(lParam).Sc'我怀疑消息频繁到达导致这里发生阻塞<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Case 2<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Case Else<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; End Select<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Case Else<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br/>&nbsp;&nbsp;&nbsp; End Select<br/>'&nbsp; ‘调用原来的窗口过程<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WindowProc = CallWindowProc(prevWndProc, hw, uMsg, wParam, lParam) 'lpPrevWndProc<br/>End Function<br/>’---------------------使用时-------</font></p>
<p><font face="Verdana">Private Sub Form_Load()<br/>&nbsp;&nbsp;&nbsp; Me.Tag = Hook(Me.hwnd)<br/>End Sub</font></p>
<p><font face="Verdana">Private Sub Form_Unload(Cancel As Integer)<br/>&nbsp;&nbsp;&nbsp; Unhook Me.hwnd, Me.Tag<br/>End Sub</font></p>
<p>'-----------------------------------------在线程里面:</p>
<p>&nbsp;</p>
<p><font face="Verdana">Private Sub Timer1_Timer()<br/>&nbsp;&nbsp;&nbsp; Dim lResult As Long<br/>&nbsp;&nbsp;&nbsp; Dim i As Integer<br/>&nbsp;&nbsp;&nbsp; '给主窗口发送消息。<br/>&nbsp;&nbsp;&nbsp; If Sc &lt; 10 Then<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Sc = Sc + 1<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lParam = MyIndex<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wParam = 1<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RaiseEvent SetSc(Sc)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lResult = PostMessage(mHwnd, WM_USER + 1, wParam, lParam)<br/>&nbsp;&nbsp;&nbsp; Else<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Sc = 0<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Timer1.Enabled = False<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TimBl = False<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RaiseEvent ImBusy(TimBl)<br/>&nbsp;&nbsp;&nbsp; End If<br/>&nbsp;&nbsp;&nbsp; For i = 0 To 100<br/>'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Text1(index).Text = i<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lParam = MyIndex<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wParam = 1<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RaiseEvent SetSc(i)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lResult = PostMessage(mHwnd, WM_USER + 1, wParam, lParam)'发出阻塞的源头。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Sleep 1<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DoEvents<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; If IsStop = True Then Exit For<br/>&nbsp;&nbsp;&nbsp; Next i<br/>End Sub</font></p>
<p>’------------------</p>
<p>&nbsp;</p>
<p>结果在VB编辑器里面运行发现是单线程的,没有发生消息阻塞,编译后再运行,发现已经类似多线程了,但是主窗口消息被阻塞,基本不响应按钮单击等事件了。</p>
<p>&nbsp;</p>
<p>在实际使用过程中,如果连接的客户端很多的发,消息发送频率就会有上面的那么高。我上面只模拟了6个线程。</p>
<p>&nbsp;</p>
<p>不知老马解决这个的好办法?</p>
<p>&nbsp;</p>

jessezappy 发表于 2009-11-8 15:24:12

<p>附上面提到的一套源代码。老马帮忙看下有什么问题,不知道我写对了没有。。</p>
<p><br/></p>
<p>上面代码的设想是将客户端发送过来的数据送来主窗口显示。虽然这个显示不是必须的,但是如果管理员要看客户端发来数据显示的话,就显示一下,不要的话就不显示。</p>
<p>&nbsp;</p>
<p>上面的代码应该还可以证明一个问题:多线程下Timer控件内部的循环阻塞没有单线程下面的“严重”。在VB编辑器里面直接运行代码就可以看到单线程下Timer控件之间的“阻塞”是及其严重的。</p>
<p>&nbsp;</p>
<p>'--------------------------------------</p>
<p>&nbsp;</p>
<p>刚刚用昨天说的对象数组事件定义整合在上面的代码中试了一下,感觉可行,线程间调度开销暂时不考虑下,我屏蔽了那个消息钩子,程序动作和上面的是一样的。代码也请老马帮忙核查一下看看存在的问题。</p>
<p></p>
<p>这个代码可以流畅运行了。在独立运行时线程间的Timer时钟控件之间不会发生阻塞,主窗口也能动,在VB编辑器里面还是同样的,线程中的Timer相互阻塞,如果6个线程都交给任务过去需要的运行完成时间是很长的,在编译后独立运行时则基本只需要一个线程运行完成的时间就够了。这个应该算是多线程了吧?</p>
<p>接下来我准备测试线程里面的 Sock 控件了。期待它能正常工作。。。。。。</p>
<p>&nbsp;</p>
<p>’---------------------------------发生个及其郁闷的问题,线程里面的Sock“似乎”不能及时进行应答 .Accept 导致客户端反复提交连接请求。。。。。。实在郁闷啊。。明早再想办法了。。。。。<br/></p>
[此贴子已经被作者于2009-11-8 23:29:12编辑过]

马大哈 发表于 2009-11-9 01:29:11

<p>有几个问题.</p>
<p>&nbsp;</p>
<p>一,单元线程里面不需要doevents调用,因为本身并无界面,无需要释放控制权.</p>
<p>&nbsp;</p>
<p>二,更新到主界面的参数等信息,用不着拼命发,每次保证在1秒内能发10个左右,已经是很不错的界面体验了.</p>
<p>&nbsp;</p>
<p>三,使用事件来通讯,与使用SendMessage是一样的效果,调用返回前,调用线程是挂起状态,等着处理完成后返回.</p>
<p>&nbsp;</p>
<p>既然要通讯,线程间的调度肯定是免不了的,但要尽量地减少调度的频率,以防性能被严重影响.</p>
<p>&nbsp;</p>
<p>不过MSDN里虽然说了,开销极大,线程间调度"几乎与进程间调度一样慢",但我自己并未实测其对具体性能的影响,建议你先不考虑这个性能问题,直接线程间调度看看,比较一下性能差.</p>
<p>&nbsp;</p>
<p>说不定这个性能差是可接受的范围内,那就OK了呢,代码还简单,呵呵......毕竟,VB6是11年前的产品......现在的硬件性能来跑这些应用或许没有当年MSDN中所描述的那么明显吧:)</p>

jessezappy 发表于 2009-11-9 12:57:35

<div class="msgheader">QUOTE:</div><div class="msgborder"><b>以下是引用<i>马大哈</i>在2009-11-9 1:29:11的发言:</b><br/>
<p>有几个问题.</p>
<p>&nbsp;</p>
<p>一,单元线程里面不需要doevents调用,因为本身并无界面,无需要释放控制权.</p>
<p>&nbsp;</p>
<p>二,更新到主界面的参数等信息,用不着拼命发,每次保证在1秒内能发10个左右,已经是很不错的界面体验了.</p>
<p>&nbsp;</p>
<p>三,使用事件来通讯,与使用SendMessage是一样的效果,调用返回前,调用线程是挂起状态,等着处理完成后返回.</p>
<p>&nbsp;</p>
<p>既然要通讯,线程间的调度肯定是免不了的,但要尽量地减少调度的频率,以防性能被严重影响.</p>
<p>&nbsp;</p>
<p>不过MSDN里虽然说了,开销极大,线程间调度"几乎与进程间调度一样慢",但我自己并未实测其对具体性能的影响,建议你先不考虑这个性能问题,直接线程间调度看看,比较一下性能差.</p>
<p>&nbsp;</p>
<p>说不定这个性能差是可接受的范围内,那就OK了呢,代码还简单,呵呵......毕竟,VB6是11年前的产品......现在的硬件性能来跑这些应用或许没有当年MSDN中所描述的那么明显吧:)</p></div>
<p>感谢老马再次指点。</p>
<p>事件通讯和SendMessage是一样的话,就有点麻烦了。。。。。</p>
<p>我还是先用事件通讯来做做看。。。现在就是Sock使用中发现问题。似乎没有及时应答。。。。正在想办法解决中。。。</p>

jessezappy 发表于 2009-11-9 13:22:40

<p>经测试。在线程的时钟控件里面:</p>
<p><font face="Verdana">Private Sub Timer1_Timer()<br/>&nbsp;&nbsp;&nbsp; Dim lResult As Long<br/>&nbsp;&nbsp;&nbsp; Dim i As Long<br/>&nbsp;&nbsp;&nbsp; '给主窗口发送消息。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp; For i = 0 To 100000<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; If i = Int(i / 997) * 997 Then _<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RaiseEvent SetState(MyIndex, 0, TimBl, i, "")<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; If IsStop = True Then Exit Sub<br/>&nbsp;&nbsp;&nbsp; Next i</font></p>
<p>&nbsp;</p>
<p>end sub</p>
<p>或‘---------------</p>
<p><font face="Verdana">Private Sub Timer1_Timer()<br/>&nbsp;&nbsp;&nbsp; Dim lResult As Long<br/>&nbsp;&nbsp;&nbsp; Dim i As Long<br/>&nbsp;&nbsp;&nbsp; '给主窗口发送消息。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp; For i = 0 To 100000<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RaiseEvent SetState(MyIndex, 0, TimBl, i, "")<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; If i = Int(i / 997) * 997 Then DoEvents<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; If IsStop = True Then Exit Sub<br/>&nbsp;&nbsp;&nbsp; Next i</font></p>
<p>end sub</p>
<p>’这两种方法导致的CPU开销差距是极大极大的。</p>
<p>说明 RaiseEvent&nbsp; 方式如果频繁调用的话,是会极大的占用系统资源的。</p>
<p>前面一种方式,运行循环997次后调用一次 RaiseEvent&nbsp; ,CPU占用最大只有2%。</p>
<p>后一种方式尽管使用了 DoEvents 但是基本没有效果,反倒导致线程处理速度变慢,而主界面明显感到迟缓,CPU占用达55~65%之间。</p>
<p>因此,频繁调用 RaiseEvent&nbsp; 是不可取的。老马的指导有道理。</p>
<p>我测试的机器配置:CPU: Core Duo E8200 2.66GHz,内存 2G。</p>

jessezappy 发表于 2009-11-9 14:34:14

<p>经过反复测试,发现,线程里面的Sock不能保持连接还是怎么的,在客户端那边看来是连接上又断开了。即使将侦听Sock和接收应答的Sock放在线程也不行。因为接收应答的Sock如果和侦听Sock不在同一个线程的话,好像连接就不能保持了,客户端总是收到 <font face="Verdana">_Close</font> 事件。。。无奈啊。。。刚才搜索到一个 PowerTCP的&nbsp;Winsock for ActiveX 。收费的。不晓得能不能解决这个问题。</p>

马大哈 发表于 2009-11-9 15:25:20

<p>也许你可以使用一下这个API版的WINSOCK控件来代替一下系统的WINSOCK控件看看......</p>
<p>&nbsp;</p>
<p>因为VB6的很多控件都不是为了多线程而考虑的,所以在多线程下出问题也是极有可能的...</p>
<p>&nbsp;</p>
<p>只不过我曾经写的那个多线程+WINSOCK下载例子,工作是正常的,不明白为什么你的WINSOCK会断开....</p>

马大哈 发表于 2009-11-9 15:40:12

<p>我受不了这服务器了...........</p>
<p>&nbsp;</p>
<p>我要去另外找个空间.............</p>

jessezappy 发表于 2009-11-9 20:15:44

<div class="msgheader">QUOTE:</div><div class="msgborder"><b>以下是引用<i>马大哈</i>在2009-11-9 15:40:12的发言:</b><br/>
<p>我受不了这服务器了...........</p>
<p>&nbsp;</p>
<p>我要去另外找个空间.............</p></div>
<p>老马在线啊。。</p>
<p>我也受不了这个服务器了,有时奇慢。。</p>
<p>经过我刚才的反复测试,因为在VB编辑器里面是单线程运行的,多线程“生”不出来,所以在VB编辑器里面运行程序和编译后再运行产生多线程结果千差万别。</p>
<p>所以,我测试的结果是在VB编辑器里面运行一切正常,但是在编译后运行的话,Sock的C<font face="Verdana">onnectionRequest方法得到的<font face="Verdana">requestID</font>如果传递给另一个线程的Sock去Accept的话,就会出现上面提到的问题。前面说错了,应该是 <font face="Verdana">.Listen</font> 的Sock 只要和 <font face="Verdana">.Accept requestID</font> 的Sock在同一个线程里面就可以正常通讯,但是,如果将 requestID&nbsp; 传递给别个线程的 Sock去处理 <font face="Verdana">.Accept</font>&nbsp; 的话,就不能工作了,总是自动断开连接。而且连接断开时不发生 <font face="Verdana">_Close</font> 事件。</font></p>
<p>也就是说。。。。requestID&nbsp; 不能跨线程去 <font face="Verdana">.Accept</font>&nbsp;&nbsp; 。。。。。。API的办法我也试试。</p>
<p>刚刚测试了得出上面的结论。老马你那个 多线程例子我分析了一下,因为你只建立了一个线程,在线程中使用的是 Sock控件数组,因此不会发生我上面的问题,我测试了一下那个多线程下载例子工作正常。</p>

jessezappy 发表于 2009-11-9 21:54:39

<p>又测试了几次Sock数组的“多线程”发现。。。Winsock 也是单线程的。请看如下例子:</p>
<p>'这是一个Sock控件数组的数据到达事件。</p>
<p><font face="Verdana">Private Sub myWSock_DataArrival(Index As Integer, ByVal bytesTotal As Long)<br/>&nbsp;&nbsp;&nbsp; Dim gdt As String<br/>&nbsp;&nbsp;&nbsp; Dim i As Long<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; myWSock(Index).GetData gdt<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; '--------------------------数据到达,传回主线程<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RaiseEvent SetState(Index, 2, TimBl, 0, gdt)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; myWSock(Index).SendData "OK"<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; If gdt = "run" Then ' 进入耗时循环<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; For i = 0 To 50000000<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; If i = Int(i / 99763) * 99763 Then<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; myWSock(Index).SendData Trim(Str(i))<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; End If<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; If IsStop = True Then Exi</font><font face="Verdana">t Sub<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Next i<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; myWSock(Index).SendData vbCrLf &amp; "Run Over"<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RaiseEvent SetState(Index, 2, TimBl, 0, "Run Over")<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; End If&nbsp;&nbsp;&nbsp;&nbsp; <br/><br/>End Sub</font></p>
<p>&nbsp;</p>
<p>'----------------------------------------------</p>
<p><font face="Verdana">如果Sock控件数组是多线程,那么在掉入这个 run 标记的循环后,它应该还能响应:</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RaiseEvent SetState(Index, 2, TimBl, 0, gdt)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; myWSock(Index).SendData "OK"<br/>这两句数据到达确认。</font></p>
<p><font face="Verdana">我用了两个TCP客户端测试,在第一个客户端发送 run 过去后,第二客户端就随便发点东西过去。结果,一直等到:<br/>&nbsp;'---此时index=1<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; myWSock(Index).SendData vbCrLf &amp; "Run Over"<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RaiseEvent SetState(Index, 2, TimBl, 0, "Run Over")<br/>这两句运行过了,才轮到上面第二个客户端发送的数据到达显示:<br/>&nbsp;'------此时 index = 2<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RaiseEvent SetState(Index, 2, TimBl, 0, gdt)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; myWSock(Index).SendData "OK"<br/>'....................<br/>由此应该可以看出sock控件数组也是单线程的。或者要说它是多线程的,那么它也是相互间会严重阻塞的。</font></p>
页: [1] 2
查看完整版本: 【求助】创建多线程的问题