问题描述:
通过iterator访问动态list,无法访问list中所有数据。我在两个线程中,线程1通过UDP接收数据动态的向LIst中添加数据,线程2实时动态遍历List中的数据,发现虽然在线程1中所有的数据均已添至List中,但是线程2中却无法成功遍历List中所有的数据,代码如下:
void *pthread1(void *arg) {
WORD sockVersion = MAKEWORD(2, 2);
WSADATA wsadata;
if (WSAStartup(sockVersion, &wsadata) != 0)
{
cout << "WinSock startup failed!";
return 0;
}
SOCKET slisten = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
int timeOutMillSeconds = 5000;
if (setsockopt(slisten, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeOutMillSeconds, sizeof(int)) == -1) {
perror("setsocketopt failed");
}
sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(receiveHVPort);
sin.sin_addr.S_un.S_addr = INADDR_ANY;
if (::bind(slisten, (SOCKADDR*)&sin, sizeof(sin)) == SOCKET_ERROR)
{
cout << "bind Receive_VTD socket error!" << endl;
::system("pause");
return 0;
}
sockaddr_in remoteddr;
int nlen = sizeof(remoteddr);
char recvBuff[256];
while (true)
{
int ret = recvfrom(slisten, recvBuff, sizeof(recvVTDBuff), 0, (SOCKADDR*)&remoteddr, &nlen);
if (ret > 0)
{
::memcpy(((char *)&HVGnss)+24, recvVTDBuff, 192);
//cout << "Hv.Speed" << Hv.speed << endl;
if (recordFlag != 1 && scenarioStatus == 1)
{
recvGNSSCount++;
GnssList.push_back(HVGnss);
}
}
}
}
void* pthread2(void* arg){
list<UDPPositionData>::iterator HVGnssIndex = GnssList.begin();
UDPPositionData gnssData;
int firstFlag = 1;
while(true){
if (GnssList.size() != 0){
if (firstFlag ){
firstFlag = 0;
HVGnssIndex = GnssList.begin();
}
gnssData = *HVGnssIndex ;
cout << gnssData.X << endl;
if (HVGnssIndex != GnssList.end()){
HVGnssIndex++;
}
Sleep(100);
}
}
}
注意上面线程2中,iterator 赋值了两次List.begin(),若不在循环体中再次进行赋值,则无法遍历到List中的数据,原因与下相同。
原因分析:
iterator其实就是指针,它指向list中数据所在的地址,当它指向空list的begin()或者list.end(),那么其指针地址便指向了C++默认分配给空数据的地址,测试代码如下:
int main() {
list<int> testList;
list<int>::iterator testIndex = testList.begin();
testIndex = testList.begin();
testList.push_back(1);
testList.push_back(2);
cout << *testIndex << endl;
for (testIndex = testList.begin(); testIndex != testList.end(); testIndex++)
{
cout << *testIndex << endl;
}
testList.push_back(3);
testList.push_back(4);
testIndex++;
cout << *testIndex << endl;
}
对于第一个cout << *testIndex << endl;
我们期望它输出的是1,但是程序运行到这里就会意外终止了,报错情况如下:
这便是由于第一次给iterator进行赋值时,此时list中还为空,那么返回给iterator的地址则是一个空数据地址,虽然后续向list中添加了数据,但是iterator的指向仍未变,这时直接进行取值自然就报错了。当把第一个cout << *testIndex << endl;
注释掉,代码如下,发现其运行结果如下:
int main() {
list<int> testList;
list<int>::iterator testIndex = testList.begin();
testIndex = testList.begin();
testList.push_back(1);
testList.push_back(2);
//cout << *testIndex << endl;
for (testIndex = testList.begin(); testIndex != testList.end(); testIndex++)
{
cout << *testIndex << endl;
}
testList.push_back(3);
testList.push_back(4);
testIndex = testList.begin();
testIndex++;testIndex++;
cout << *testIndex << endl;
其执行结果如下:
如此运行便正常了。
解决方案:
所以在对类似这种动态的向list中插入数据并进行实时访问数据时,一定要注意当前iterator是否依然是初始化时的空list的begin()或者已经指向了list的end()。
对于前者,要在list不为空后重新将iterator指向list.begin(),而对于后者,可以加一个遍历计数器,若遍历到的数据数小于list.size()则将iterator重新指向list.begin(),并进行已遍历“数据数量”次自加操作,这样iterator便接着上次遍历时的最后一个数据继续遍历了。
其实对于我这种多线程对于同一变量的读写操作,也可以加互斥锁来解决问题,严格控制执行顺序便好了。