Chinaunix

标题: STL自己的函数也会出错!!? [打印本页]

作者: jerryzheng    时间: 2004-07-13 15:17
标题: STL自己的函数也会出错!!?
本人在做一个压力测试的时候发现错误,最后找了半天发现竟然是STL本身函数的问题,下面是出错部分代码,很简单:
A线程
while(!sendqueue.empty())
{                                       
   try
  {
        string &tempbuf = handlequeue.front();
  }
catch (...)
{
  MessageBox(0,"handlequeue.front()存在问题",0,0);
  }
.....
                                               
问题就出在front函数,handlequeue是一个queue<string>;的队列,B线程在往里面push数据,A线程在取数据,当处理极快的时候B线程没有异常,会出现handlequeue.front()异常抛出!        是什么原因造成的?怎么解决呢?
作者: jerryzheng    时间: 2004-07-13 15:34
标题: STL自己的函数也会出错!!?
起初我想是不是B线程处理太快以至于handlequeue 的空间扩张太快而遇到问题,但是B线程应该不会比A线程更快,因为B线程里处理流程比较长执行函数比较多。后来我想难道 queue不是线程安全的?但是push只是向尾部插入,而front是从头布取出,如果取完了,就为空(当取出的数据处理了以后我用pop了),等待信号灯置亮(每次push的时候我会把一个信号置亮,等处理完既handlequeue为空的时候把信号熄灭),不过可能问题都不是上者。最奇怪的还是一旦出现front()的异常,就会永远停留在while中总是出现front异常,队列既不为空,front又不能成功,似乎整个handlequeue崩溃掉了。
作者: 我不懂C++    时间: 2004-07-13 16:32
标题: STL自己的函数也会出错!!?
STL不是线程安全的。你的同步可能有问题,说详细点。
至于STL代码是否正确,我可以说没有完全正确的代码,但是至少在这个层面上,应该不会错的。
作者: linux_newbie    时间: 2004-07-13 16:46
标题: STL自己的函数也会出错!!?
stl不是线程安全的。因此不能在没有保护的情况下,在多线程环境中使用。

但是push只是向尾部插入,而front是从头布取出,如果取完了,就为空(当取出的数据处理了


没有什么但是。即使一个简单的全局变量加一的操作,如果没有专门的保护,在多线程环境中都有错误,更加别提什么push, pop了。[/quote]
作者: jerryzheng    时间: 2004-07-13 16:54
标题: STL自己的函数也会出错!!?
见下贴
作者: jerryzheng    时间: 2004-07-13 17:06
标题: STL自己的函数也会出错!!?
B线程中关于这段的代码       
   readysend=HandleMsgFunc1(recvbuffer.c_str()); //  readysend是string全局变量
if(!readsend.empty())
{
      handledqueue.push(readsend);
      SetEvent(SendEvent);       
}
A线程:
while(WAIT_OBJECT_0 == WaitForSingleObject(m_hHandleEvent,INFINITE))
{
while(!sendqueue.empty())
{
try
{
string &tempbuf = handlequeue.front();
}
catch (...)
{
MessageBox(0,"handlequeue.front()存在问题",0,0);
}
....
handledqueue.pop();
ResetEvent(sendevent);

}
B线程是从网络接受来的消息进行处理后放进一个已处理队列,
A线程负责从这个已处理队列中取出元素来发送到网络。

压力测试:做一个工具,死循环发送100字节的随机内容的包给接受的服务器,中途不sleep,但只有从网络受到回复才做下一次发送,也就是说接受和发送是阻塞的。一个工具一秒大约能收发共500-1000条消息;服务器端只是把收到的包简单作了一个回复。结果发现,当同时开几个工具一起向一台服务器发送的时候,出现问题,问题就出在STL 的队列front()函数!但抛出异常的时间不等,有的时候30秒,有的时候1,2分钟,但不超过3分钟,一般开3-4个压力工具就出现问题。
作者: 我不懂C++    时间: 2004-07-13 17:06
标题: STL自己的函数也会出错!!?
实在看不懂,花括号好像不匹配吧?
作者: jerryzheng    时间: 2004-07-13 17:11
标题: STL自己的函数也会出错!!?
括号不会少的,可能是我贴少了,程序就是这样,帮我分析一下
作者: babizhu    时间: 2004-07-13 17:20
标题: STL自己的函数也会出错!!?
同意 我不懂C++ 的观点
虽然没有看你的source,不好下结论,但是问题出在你自己代码的可能性应该是远远大于stl自己有问题的可能性。
问题十之八九出在多线程的同步上,建议仔细看一下event的相关内容
作者: jerryzheng    时间: 2004-07-13 17:44
标题: STL自己的函数也会出错!!?
感谢各位大大的意见!目前在原来信号的基础上加了互斥,以控制对队列的同时访问,压力测试正在进行中,目前4个压力工具同时开还没出现问题,大约一秒1500-2000条消息
作者: jerryzheng    时间: 2004-07-13 17:50
标题: STL自己的函数也会出错!!?
我晕!10分钟的时候死了,还是front()有错误!临界区,信号,mutex都已经在各种场合用了,还是死!不得不相信STL有问题了,或者只VC的STL有问题?我是在vc下写的代码。曾经出现过dbgheap.c文件中某处断言有问题,那是系统定义的文件
作者: 我不懂C++    时间: 2004-07-13 21:11
标题: STL自己的函数也会出错!!?
你把完整代码给我,只要是C++的,我有信心帮你搞定
作者: jerryzheng    时间: 2004-07-14 09:40
标题: STL自己的函数也会出错!!?
如果handlequeue 崩溃的话,如何能重新让它恢复使用
作者: linux_newbie    时间: 2004-07-14 10:13
标题: STL自己的函数也会出错!!?
你贴的代码根本就没有保护handledqueue,不死才怪。
作者: soloarrow    时间: 2004-07-14 10:19
标题: STL自己的函数也会出错!!?
[quote]原帖由 "jerryzheng"]我晕!10分钟的时候死了,还是front()有错误!临界区,信号,mutex都已经在各种场合用了,还是死!不得不相信STL有问题了,或者只VC的STL有问题?我是在vc下写的代码。曾经出现过dbgheap.c文件中某处断言有问题,那?.........[/quote 发表:


我觉得还是你的程序本身有问题的可能性多一点。因为不做压力测试大的时候你的程序是正常的嘛,做压力测试(且要到一定的并发程序)的时候才会出错,所以我个人认为还是线程并发上出现问题的可能性大一点。

PS:vc本身也有bug,对c++有很多自己的定义,对stl的支持也有可能不完美。能不能换个unix环境试试?
作者: jerryzheng    时间: 2004-07-14 10:21
标题: STL自己的函数也会出错!!?
有信号控制,压进消息的时候会把信号置亮,取出消息发送完成后会把信号熄灭,后来听取一些人的意见,又加了互斥,限制同时访问队列,我上面的回复已经说得比较清楚了吧,问题依然出现。当有压力测试工具加上一个sleep(50)的时候不会死(速度慢的原因吧),如果不sleep就会死,发送太快,服务器处理不过来?表现出来的异常就是队列的front函数有异常!我现在想知道的是:1,如果队列崩溃,如何恢复它;2,这个异常有没有方法可以看见更详细的信息
作者: linux_newbie    时间: 2004-07-14 10:24
标题: STL自己的函数也会出错!!?
那就是互斥没有加对。代码贴上了一看就知。
作者: jerryzheng    时间: 2004-07-14 10:31
标题: STL自己的函数也会出错!!?
加互斥很简单,不会没加对吧,如果真是没加对,那么怎么解释如果设一段sleep就不会死的问题,还有怎么会出现front()异常的原因,我觉得是不是push函数并不安全,可能会造成错误的内存分配(可能在push,front,pop非常快的情况下都会出现异常),造成front抛出异常?
作者: 我不懂C++    时间: 2004-07-14 10:42
标题: STL自己的函数也会出错!!?
这个,如果你怀疑VC的STL有问题,你可以去下载一个SGI STL用用看,如果还是有问题,就看自己的代码吧。
VC的deque实现代码也有,你不如自己读一下,应该不会有问题。
还是那句话,虽然VC很烂,但是程序员出了问题,应该先怀疑自己,再怀疑环境。
你既然说:“加互斥很简单,不会没加对吧”我也可以说,写deque很简单吧,PJ不会没写对吧。
作者: linux_newbie    时间: 2004-07-14 10:43
标题: STL自己的函数也会出错!!?
多线程程序的错误就是随机发生的。没有什么为什么。
push这么简单的实现,你还真当为microsoft写STL的那个叫什么P.J的是白痴?

不贴代码,基本上是“空对空”。
作者: jerryzheng    时间: 2004-07-14 11:13
标题: STL自己的函数也会出错!!?
让我再考虑一下,虽然原理简单,但是里面的实现牵扯到几个线程及其他问题,不是贴2个线程的相关函数就能看明白的,也不让把所有代码贴上来。现在我只有再仔细想想了。另外问一下,代码贴到里面来格式就变了,怎么编辑?
作者: whyglinux    时间: 2004-07-14 21:35
标题: STL自己的函数也会出错!!?
1. 要保证在任一时刻最多只能有一个线程访问  handledqueue。上面各个帖子主要是就这一点讨论的,不再赘述。

2. 一般要对 handledqueue 的容量进行控制。超过之后,不应该再继续向其送数据。如果第 1 步做好了,那么问题很可能出现在这里,可做一下这方面的处理。

3. handlequeue.front()这样的操作不会抛出异常,所以你捕捉它的异常是没有意义的(对于VC++,我不敢肯定,你试试是否如我所说的那样)。一般的 STL 的实现注重的是效率,所以很少会进行异常处理,除非你使用的是安全版本的 STL。一般 STL 常见的异常一个是在内存方面的异常(如内存分配错误),一个是 vector 或者 deque 中使用 at() 的越界异常。所以我对你说的会出现handlequeue.front()异常抛出表示怀疑。你还没有对错误正确定位。

catch( std::exception& e ),查看 e.what() 了解产生异常的原因。
作者: dxz    时间: 2004-07-15 00:57
标题: STL自己的函数也会出错!!?
昏,不就是queue是空了吗? 空了调用front肯定抛出异常呀。
作者: whyglinux    时间: 2004-07-15 10:32
标题: STL自己的函数也会出错!!?
>;>; 昏,不就是queue是空了吗? 空了调用front肯定抛出异常呀。

如果queue为空,则调用 front() 的行为是“未定义”(undefined)的,但是肯定不会抛出异常(除非真的对 front() 实现了异常处理)。用户应该保证调用 front() 时 queue 不为空,这是使用它的先决条件。

楼主的程序中保证了这一点,即在 queue 不为空的情况下进行 front() 操作。所以说错误并不是如你所说的“queue 为空,front() 抛出异常”。
作者: aXe    时间: 2004-07-15 11:24
标题: STL自己的函数也会出错!!?
八成是你自己的问题。

画个图先。
作者: jerryzheng    时间: 2004-07-15 12:53
标题: STL自己的函数也会出错!!?
whyglinux说得有道理,对同一时刻只能一个线程访问这点是已经保证了的。你说的第二个出问题的地方我昨天正好实验了一下,由于队列是作为处理线程的成员变量所以我在createthread(NULL,0..)把第二个参数改为5M,默认是1M,结果发现情况好转,由于这两天做些别的事情,还没来得及进一步验证!还想请问一下,有没有更好的控制队列容量的方法
作者: 我不懂C++    时间: 2004-07-15 14:01
标题: STL自己的函数也会出错!!?
to jerryzheng:
至少你在前一次给出的代码还是错的。或者说,正如linux_newbie说得那样,不给代码就是空对空。出于对大家时间的负责,也为了让你快点解决问题,你应该给出一个例子,可以再现这个问题的。
至于限制queue的大小,我觉得在这里不一定能解决问题,毕竟你内存应该还是够的。何况你在CreateThread里面增大的只是栈,而通过这样的增大如果能够解决问题的话,也是暂时的。
作者: linux_newbie    时间: 2004-07-15 14:41
标题: STL自己的函数也会出错!!?
呵呵。大伙还在这里干着急啊。

关于多线程程序,绝大部分情况,多线程程序需要的是一种类似“精神分裂”的思维,脑子里面不停地想像线程可能在任何时间被切换。它需要在设计阶段就先考虑好,而不能过多地期望通过测试来发现问题。

写多线程程序如果能够调试出错,还能够重现这个错误,那已经是错的无可救药了。

原则就是,如果有问题,那么就一定程序有问题。如果没有问题,那么可能有问题。

例如一个简单的例子:
定义一个全局变量int g_count = 0,如果没有线程保护,那么如果同时在二个不同的线程中调用g_count++一次,最后的结果都有可能是1,而不是2。但是你测试的时候可能一辈子都不会发生。

一个复杂点的多线程程序运行半年正常,然后半夜突然crash并不稀奇,而且几乎无法重现这个错误,只有一行行看源代码。
作者: 我不懂C++    时间: 2004-07-15 14:47
标题: STL自己的函数也会出错!!?
如果:
g_count是32位的
你只有一个CPU
你的CPU是32位的
你的编译器厂商雇佣的人员脑子没问题

g_count++是安全的。
作者: linux_newbie    时间: 2004-07-15 15:40
标题: STL自己的函数也会出错!!?
呵呵。这种保证不是比较脆弱的吗?
作者: F3&F5    时间: 2004-07-15 20:13
标题: STL自己的函数也会出错!!?
原帖由 "我不懂C++" 发表:
如果:
g_count是32位的
你只有一个CPU
你的CPU是32位的
你的编译器厂商雇佣的人员脑子没问题

g_count++是安全的。


不明白什么意思?

我记得Jeffrey Richter的书里面提到过++是不安全的,没有强调过什么特例,可以解释一下吗?
作者: linux_newbie    时间: 2004-07-15 20:35
标题: STL自己的函数也会出错!!?
这是因为Intel的CPU有一条指令
inc dest
其中dest既可以是一个寄存器,又可以是一个内存地址。

因此编译器如果不是那么愚蠢的话,生成的代码应该是
inc  [g_count]

但是,这有几个限制:
1. Debug版本下一般不会产生类似的代码,而是
mov ecx, [g_count]
inc ecx
mov [g_count], ecx

2. 多CPU就不灵了

3. 不是所有的CPU都有类似的指令可以直接操作内存地址。好像RISC的alpha就没有,而需要经过寄存器中转。
作者: F3&F5    时间: 2004-07-15 20:42
标题: STL自己的函数也会出错!!?
懂了,谢谢
作者: tp801    时间: 2004-07-17 11:30
标题: STL自己的函数也会出错!!?
One word: STL is NOT multi-thread safe! You had to protect it with any suitable locking machinizm.
作者: yunin    时间: 2004-07-18 23:55
标题: STL自己的函数也会出错!!?
simultaneous read access to the same container from within separate threads is safe;
simultaneous access to distinct containers (not shared between threads) is safe;
user must provide synchronization for all accesses if any thread may modify shared container.
作者: yunin    时间: 2004-07-18 23:59
标题: STL自己的函数也会出错!!?
http://www.sgi.com/tech/stl/thread_safety.html
这里有说到STLport的线程安全的问题,有兴趣的可以看看,会有帮助的。
作者: eagerly1    时间: 2004-07-19 14:37
标题: STL自己的函数也会出错!!?
假定任务为:A B A B A B A B A B A B A B A B。。。
其中A、B都是要执行的。
在单线程下,执行顺序为:A->;B->;A->;B->;A->;B->;A->;B->;A->;B...

在多线程下,可能为:A->;B->;A->;B->;A->;B。。。
也可能为:A->;A->;A->;B->;A->;B->;B->;B。。。
还有可能为:A->;B->;A->;B->;A->;A->;A->;B。。。

。。。

总之,不能确定,这就是多线程的执行随机性




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2