我正在为Linux上的(pthread)multithreadingC ++程序进行debugging。
当线程数很less的时候,它就可以工作,比如1,2,3。
当线程数增加时,我得到了SIGSEGV(分段错误,UNIX信号11)。
但是,当我增加4以上的线程数时,错误有时会出现,有时会消失。
我用valgrind,我得到了
== 29655 ==处理以信号11(SIGSEGV)的默认动作结束
== 29655 ==不在地址0xFFFFFFFFFFFFFFF8的映射区域内访问
== 29655 ==在0x3AEB69CA3E:std :: string :: assign(std :: string const&)(在/usr/lib64/libstdc++.so.6.0.8中)
== 29655 == by 0x42A93C:bufferType :: getSenderID(std :: string&)const(boundedBuffer.hpp:29)
看来我的代码试图读取未分配的内存。 但是,在函数getSenderID()中找不到任何错误。 它只会返回Class缓冲区types中的一个成员数据的string。 它已经被初始化了。
我使用GDB和DDD(GDB GUI)来查找错误,这也指出了错误,但错误有时会消失,所以在GDB中,我无法用断点捕获错误。
而且,我还打印出valgrind所指向的函数的值,但是这样做并没有什么帮助,因为多个线程以不同的顺序打印结果,并且它们相互交错。 每次运行代码时,打印输出都是不同的。
bufferType在地图中,地图可能有多个条目。 每个条目可以由一个线程写入,并由另一个线程同时读取。 我用pthread读/写锁来locking一个pthread_rwlock_t。 现在,没有SIGSEGV,但程序停止了一些没有进展的点。 我认为这是一个僵局。 但是,一个地图项只能在一个时间点上只能写一个线程,为什么还会发生死锁?
你能否推荐一些方法来捕捉这个bug,以便我可以find它,不pipe我使用多less个线程来运行代码。
谢谢
class bufferType { private: string senderID;// who write the buffer string recvID; // who should read the buffer string arcID; // which arc is updated double price; // write node's price double arcValue; // this arc flow value bool updateFlag ; double arcCost; int arcFlowUpBound; //boost::mutex senderIDMutex; //pthread_mutex_t senderIDMutex; pthread_rwlock_t senderIDrwlock; pthread_rwlock_t setUpdateFlaglock; public: //typedef boost::mutex::scoped_lock lock; // synchronous read / write bufferType(){} void getPrice(double& myPrice ) const {myPrice = price;} void getArcValue(double& myArcValue ) const {myArcValue = arcValue;} void setPrice(double& myPrice){price = myPrice;} void setArcValue(double& myValue ){arcValue = myValue;} void readBuffer(double& myPrice, double& myArcValue ); void writeBuffer(double& myPrice, double& myArcValue ); void getSenderID(string& myID) { //boost::mutex::scoped_lock lock(senderIDMutex); //pthread_rwlock_rdlock(&senderIDrwlock); cout << "senderID is " << senderID << endl ; myID = senderID; //pthread_rwlock_unlock(&senderIDrwlock); } //void setSenderID(string& myID){ senderID = myID ;} void setSenderID(string& myID) { pthread_rwlock_wrlock(&senderIDrwlock); senderID = myID ; pthread_rwlock_unlock(&senderIDrwlock); } void getRecvID(string& myID) const {myID = recvID;} void setRecvID(string& myID){ recvID = myID ;} void getArcID(string& myID) const {myID = arcID ;} void setArcID(string& myID){arcID = myID ;} void getUpdateFlag(bool& myFlag) { myFlag = updateFlag ; if (updateFlag) updateFlag = false; } //void setUpdateFlag(bool myFlag){ updateFlag = myFlag ;} void setUpdateFlag(bool myFlag) { pthread_rwlock_wrlock(&setUpdateFlaglock); updateFlag = myFlag ; pthread_rwlock_unlock(&setUpdateFlaglock); } void getArcCost(double& myc) const {myc = arcCost; } void setArcCost(double& myc){ arcCost = myc ;} void setArcFlowUpBound(int& myu){ arcFlowUpBound = myu ;} int getArcFlowUpBound(){ return arcFlowUpBound ;} //double getLastPrice() const {return price; } } ;
从代码中,你可以看到我试图使用读/写锁来保证不变。 地图中的每个条目都有一个像上面这样的缓冲区。 现在,我陷入了僵局。
Access not within mapped region at address 0xFFFFFFFFFFFFFFF8
at 0x3AEB69CA3E: std::string::assign(std::string const&)
这通常意味着你正在分配给一个NULL
string*
,然后得到递减。 例:
#include <string> int main() { std::string *s = NULL; --s; s->assign("abc"); } g++ -g t.cc && valgrind -q ./a.out ... ==20980== Process terminating with default action of signal 11 (SIGSEGV): dumping core ==20980== Access not within mapped region at address 0xFFFFFFFFFFFFFFF8 ==20980== at 0x4EDCBE6: std::string::assign(char const*, unsigned long) ==20980== by 0x400659: main (/tmp/t.cc:8)
…
因此,向我们展示boundedBuffer.hpp
的代码(带有行号),并考虑代码最终可能会以指向-8
的字符串指针结束。
你能否推荐一些方法来捕捉这个bug,以便我可以找到它,不管我用多少个线程来运行代码。
在考虑多线程程序时,你必须考虑不变量。 你应该提出断言来确认你的不变量是成立的。 你应该考虑如何违规,以及违规会导致你观察到的死后状态。
你有没有在一个线程中访问一个对象(比如一个字符串),而另一个线程正在或可能正在修改它的情况呢? 这是像这样的问题的通常原因。
看看你的bufferType的实例。
它什么时候被实例化?
如果在线程产生之前它被实例化了,然后其中的一个线程修改了它,那么你的竞争条件没有锁定。
此外,请注意任何接近或在该缓冲区类型内的静态变量 。
从外观上看,其中一个线程可能修改了getSenderID()返回的成员。
如果这些问题都不会导致您的错误,请尝试使用valgrind的drd 。