Ошибка отладки: нижний индекс выходит за пределы диапазона с помощью std::vector
Я пытаюсь исправить эту проблему, которая выглядит так, как будто я получаю доступ по индексу вне диапазона, но VS не может остановиться, где произошла ошибка, из-за чего я не могу понять, что вызывает это.
Ошибка:
Ошибка отладки! Программа: .... Файл: c:\program files\microsoft visual studio 10.0\vc\include\vector Строка: 1440 Выражение: нижний индекс строки вне диапазона
Что делает программа:
Есть две темы:
Тема 1:
Первый поток ищет (среди прочего) изменения в текущем окне, используя GetForegroundWindow()
проверка происходит не в цикле, а когда WH_MOUSE_LL
событие сработало. Данные разбиты на структуры фиксированного размера, чтобы их можно было отправлять на сервер по протоколу TCP. Первый поток и записывает данные (заголовок окна) в std::list
в текущей структуре.
if(change_in_window)
{
GetWindowTextW(hActWin,wTitle,256);
std::wstring title(wTitle);
current_struct->titles.push_back(title);
}
Тема 2:
Второй поток называется ищет структуры, которые еще не отправлены, и помещает их содержимое в char
буферы, так что они могут быть отправлены через TCP. Хотя я не знаю точно, где находится ошибка, судя по типу ошибки, она была связана со строкой или списком, и это единственный код из всего моего приложения, использующий списки / строки (остальные - обычные массивы). Также комментирование блока if, как указано в комментариях к коду, предотвращает возникновение ошибки.
BOOL SendStruct(DATABLOCK data_block,bool sycn)
{
[..]
int _size = 0;
// Important note, when this if block is commented the error ceases to exist, so it has something to do with the following block
if(!data_block.titles.empty()) //check if std::list is empty
{
for (std::list<std::wstring>::iterator itr = data_block.titles.begin(); itr != data_block.titles.end() ; itr++) {
_size += (((*itr).size()+1) * 2);
} //calculate size required. Note the +1 is for an extra character between every title
wchar_t* wnd_wbuffer = new wchar_t[_size/2](); //allocate space
int _last = 0;
//loop through every string and every char of a string and write them down
for (std::list<std::wstring>::iterator itr = data_block.titles.begin(); itr != data_block.titles.end(); itr++)
{
for(unsigned int i = 0; i <= (itr->size()-1); i++)
{
wnd_wbuffer[i+_last] = (*itr)[i] ;
}
wnd_wbuffer[_last+itr->size()] = 0x00A6; // separator
_last += itr->size()+1;
}
unsigned char* wnd_buffer = new unsigned char[_size];
wnd_buffer = (unsigned char*)wnd_wbuffer;
h_io->header_w_size = _size;
h_io->header_io_wnd = 1;
Connect(mode,*header,conn,buffer_in_bytes,wnd_buffer,_size);
delete wnd_wbuffer;
}
else
[..]
return true;
}
Моя попытка синхронизации потока: есть указатель на первый созданный блок data_block (db_main), указатель на текущий блок data_block (db_cur)
//datablock format
typedef struct _DATABLOCK
{
[..]
int logs[512];
std::list<std::wstring> titles;
bool bPrsd; // has this datablock been sent true/false
bool bFull; // is logs[512] full true/false
[..]
struct _DATABLOCK *next;
} DATABLOCK;
//This is what thread 1 does when it needs to register a mouse press and it is called like this:
if(change_in_window)
{
GetWindowTextW(hActWin,wTitle,256);
std::wstring title(wTitle);
current_struct->titles.push_back(title);
}
RegisterMousePress(args);
[..]
//pseudo-code to simplify things , although original function does the exact same thing.
RegisterMousePress()
{
if(it_is_full)
{
db_cur->bFull= true;
if(does db_main exist)
{
db_main = new DATABLOCK;
db_main = db_cur;
db_main->next = NULL;
}
else
{
db_cur->next = new DATABLOCK;
db_cur = db_cur->next;
db_cur->next = NULL;
}
SetEvent(eProcessed); //tell thread 2 there is at least one datablock ready
}
else
{
write_to_it();
}
}
//this is actual code and entry point of thread 2 and my attempy at synchronization
DWORD WINAPI InitQueueThread(void* Param)
{
DWORD rc;
DATABLOCK* k;
SockWClient writer;
k = db_main;
while(true)
{
rc=WaitForSingleObject(eProcessed,INFINITE);
if (rc== WAIT_OBJECT_0)
{
do
{
if(k->bPrsd)
{
continue;
}
else
{
if(!k)
{break;}
k->bPrsd = TRUE;
#ifdef DEBUG_NET
SendStruct(...);
#endif
}
if(k->next == NULL || k->next->bPrsd ==TRUE || !(k->next->bFull))
{
ResetEvent(eProcessed);
break;
}
} while (k = k->next); // next element after each loop
}
}
return 1;
}
Подробности:
Теперь что-то заставляет меня поверить, что ошибки там нет, потому что ошибка подстроки очень редкая. Я смог воспроизвести его только с 100% вероятностью, когда нажимал Mouse_Down+Wnd+Tab, чтобы прокручивать окна и удерживать его нажатым в течение некоторого времени (хотя это, безусловно, происходило и в других случаях). Я избегаю публиковать весь код, потому что он немного велик и путаница неизбежна. Если ошибки нет, я отредактирую пост и добавлю больше кода.
заранее спасибо
2 ответа
Здесь нет никакой синхронизации потоков. Если один поток читает из структуры, а другой пишет, он может быть прочитан во время инициализации с непустым списком, содержащим пустую строку (или что-то недопустимое, между).
Если за пределами опубликованной функции нет мьютекса или семафора, скорее всего, это проблема.
Все вычисления размера, кажется, действительны для Windows, хотя я не пытался запустить его... и <= … -1
вместо <
в i <= (itr->size()-1)
а также 2
вместо sizeof (wchar_t)
в new wchar_t[_size/2]();
немного странно
Проблема с вашим кодом заключается в том, что хотя поток 2 правильно ожидает данные, а поток 1 правильно уведомляет о них, поток 2 не препятствует потоку 1 что-либо делать с ними под его руками, пока он все еще обрабатывает данные. Типичным устройством, используемым для решения такой проблемы, является схема монитора.
Он состоит из одного мьютекса (используется для защиты данных, хранящегося в любое время при обращении к ним) и условной переменной (= событие в терминах Windows), которая будет передавать информацию о новых данных потребителю.
Производитель обычно получает мьютекс, производит данные, освобождает мьютекс и запускает событие.
Потребитель более хитрый - он должен получить мьютекс, проверить, не появились ли новые данные, затем дождаться события, используя функцию SignalObjectAndWait, которая временно освобождает мьютекс, затем обработать вновь полученные данные, а затем освободить мьютекс.