-会导致系统崩溃的不完善的程序
-破坏多线程调用规则的程序
-获得调用后不放手的程序:获得执行权限后自己不实现自己的堵塞,比如死循环等,比如需要死等其他线程提供的数据,但忘记允许其他线程能够先于自己执行的程序
-执行时间过长而会影响到其他定时需要调用的线程执行,有可能导致流式数据不能得到及时处理的线程,或者自己处理数据算法不够优化,无法完成本职工作的程序
-其他情况(很多,只有想不到没有做不到),可以通过系统联调或试运行发现排除
问题在于你的程序是调试版(Debug Version)的,不是发布版的
调试版的程序在编程机器上运行,受VC环境的支持,没有问题但放到没有装VC的机器上去运行,就暴露出问题了
说到底还是你程序问题
你在VC里把它以发布版(Release Version)的方式编译一次看看就会发现问题
_________
是不是使用过和管理员权限有关的函数,在XP下可以获得用户权限的API到VISTA下可能就没有作用你用vista的时候是用的guest权限,还是administator权限或从属administrator权限登陆的呢
建议用线程队列,这样避免因为循环中线程创建失败引起异常,
另外可能还需要一个监视状态的,如果连续多久没有接收到数据,
则自动关闭下载,避免出现因为服务端无返回或者下载死在那边(微软的IE下载貌似也存在这个问题)。
其实动态使用在其他计算机上也是可以到,应为win2k以上mfc动态库windows目录下也是有的。不是必须要静态库。
但是你编译的必须是release版,应为一般系统不带debug版本的mfc库。除非装了vc
2出问题,请把错误信息给出,帮你分析,比如编译错误,链接错误
多线程程序可能存在很多潜在的bug,如data race,dead lock,信号bug等,而这些bug一向很难调试,现在有很多论文都是基于多线程程序的调试技术的,比如model check,死锁检测,replay技术等,也有很多对应的工具,如intel的pinplay,微软的Zing等。关于这些技术和工具,如果感兴趣可以 google相应的论文进一步了解。这里我主要讲述的是我在对二进制翻译下多线程程序调试中经常使用的一些方法以及一些调试经验,虽然我的调试的是二进制翻译器,但是这些方法也同样适用于大多数多线程程序。
1、最直接的方法就是在源程序插入printf语句来打印出一些有用的变量。这种方法的优点是不用借助其他工具就可以对程序的运行进行观察,缺点是插入语句的位置、粒度等都需要调试者自己去权衡,如果插入过多的打印语句,则频繁的IO *** 作会使程序运行变慢,线程行为改变,有些bug甚至不会再出现。至于需要在什么地方插入语句,首先,只打印有必要的变量,一个语句可以打印多个变量;其次,在循环中,我们可以通过设置一些条件来降低打印的粒度,比如下面这段代码:
1 2 3 4 5 6 7 8 while(flag){ pc = getpc(); printf(“pc is:0x%x\n”, pc);//我们插入的打印语句 //do somthing using pc; }
假设我们对pc的取值很感兴趣,需要打印出所有pc取到过的值,但是大多数情况下,getpc()的返回值都同上一次的返回值相同,这样我们printf出来的就会有很多重复值。这种情况下我们可以用下面这种插桩方式来去处重复值:
1 2 3 4 5 6 7 8 9 10 11 int lastpc; //定义为全局变量或局部静态变量 while(flag){ pc = getpc(); if(pc !=lastpc){ lastpc = pc; printf(“pc is:0x%x\n”, pc); } //do somthing using pc }
这样通过一个简单的判断就可以省掉很多没有必要的输出。很多别的情形,比如我们只关心某一变量等于特定值(比如0)时其他变量的状态,我们就没有必要把改变量不等于0时的状态打印出来。总之,能省则省,只打印我们需要的。
2、利用gdb的attach功能和sleep()函数。gdb是由gnu维护的功能强大的调试工具,并且支持多线程程序的调试,可以在gdb下直接运行一个多线程程序,通过thread等命令进行调试。但是很多多线程程序在其他工具(gdb,pin,strace等)监管下,原有的bug就不会出现。这的确是很让人头疼的事情,也是我十分不喜欢这个方法的原因,想象一下,一个程序直接跑就出错,但是放到gdb下就能得到正确的结果,好像故意在耍我们一样。我更喜欢使用gdb的attach功能,我们可以通过下面的命令来让gdb接管一个运行的线程:
1 gdb attach <pid>
这种方法的好处是能够使gdb对程序执行的影响最小,而且可以只接管程序中某一条我们所关心的线程,而其他线程不受影响。
这时有人会问,如果线程执行过快,我们还没来得及attach线程就已经执行完或者dump掉了,这种情况该怎么办?解决方法很简单,既然线程执行过快,我们就让它等一等,可以在源代码中让我们关心这个线程sleep()一小会儿,这样我们就有足够的时间来attach它,并且attach的位置我们也可以进行控制,想在哪里attach,就在哪里sleep。
3、第三种方法是利用信号处理函数来获取一些信息。在多线程程序的压力测试中,很多错误要每隔几百几千次运行才能出现一次,而这种错误的replay是很困难的,因此捕捉到这种错误的现场很重要。这里我习惯利用信号处理程序来保存这样的现场,这样你可以晚上写个脚本让程序无限跑,早上起来你会发现程序停在出错的地方,这是很惬意的事情。
多数多线程程序出错,都是访问非法内存,也就是我们常说的“段错误”(segmentation fault),程序发生非法内存的访问,系统会发给线程一个SIGSEGV信号,这个信号默认处理为core掉该线程。我们可以对这个信号进行利用,为其注册一个信号处理函数:
1 2 3 4 struct sigaction act; actsa_flags = SA_SIGINFO; actsa_sigaction = signal_handler; sigaction(SIGSEGV, &act, NULL); //SIGSEGV表示该信号的值
信号处理函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 void signal_handler(int host_signum, siginfo_t info, void puc){ structucontext uc = (struct ucontext )puc; int loopflag = 1; while(loopflag)//可以在gdb中手动更改loopflag的值跳出循环 sleep(1); //这里可以打印一些感兴趣的变量 }
函数参数中,puc是一个体系结构相关的指针,不同的体系结构,指针指向的结构不一样,里面存放了发生信号时线程的寄存器的值,程序地址等信息,函数内第一句话的目的就是把void类型转换成ucontext结构类型,这样在gdb中可以直接print出该结构的成员。
函数中sleep的作用是让程序停在信号处理程序中,以给我们足够的时间进行attach。如果想让程序继续运行下去,手动把loopflag修改为1即可。用while循环的目的是我们可以在运行时手动控制sleep的时间。
这种方法同样适用于其他信号带来的bug,比如SIGBUS等。在二进制翻译下,还可以使用这种方法对二进制翻译器信号处理进行跟踪和调试,具体使用读者可以自己去发掘。
4、利用strace得到我们关心的信息。大多数情况下我们用strace的目的是跟踪系统调用,但其实strace对多线程程序的调试有很大的帮助,使用strace打印多线程程序信息的命令如下:
1 strace -F /test
如果我们对某些系统调用,如gettimeofday,ioctl不感兴趣,可以屏蔽掉
1 strace -F -etrace=\!gettimeofday,ioctl /test
通过strace打印出的信息,我们可以对什么时候产生了一个子线程,那个线程在等待,哪个线程被唤醒,哪个线程收到信号,哪个线程core掉有一个综合的了解,这些信息对多线程调试会起到很大的作用。
还有很多方法比如利用core文件等,很多地方可以查到,我不做累赘的介绍。总之技术是死的,但是方法是灵活的,当传统方法解决不了一个问题的时候,可以放开思路尝试其他的方法。
以上就是关于什么样的程序 多线程 会有问题全部的内容,包括:什么样的程序 多线程 会有问题、多线程程序在Vista下遭遇异常。、Java多线程问题总结等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)