C++ List遍历异常

问题描述:

  通过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;注释掉,代码如下,发现其运行结果如下:

  可以看出,控制台中已经将list中的1,2输出了出来,但此时iterator在经历一个for循环后,已经指向了此时list.end(),即使后续又向list中插入了数据,但是iterator指向的地址仍为未分配的地址,所以进行++操作会报错。当把代码改成如下:
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便接着上次遍历时的最后一个数据继续遍历了。
  其实对于我这种多线程对于同一变量的读写操作,也可以加互斥锁来解决问题,严格控制执行顺序便好了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值