2015-02-07 4 views
4

У меня есть std::thread функцию, призывающие fopen загрузить большой файл в массив:убийство станд :: нить при чтении больших файлов

void loadfile(char *fname, char *fbuffer, long fsize) 
{ 
    FILE *fp = fopen(fname, "rb"); 
    fread(fbuffer, 1, fsize, fp); 
    flose(fp); 
} 

Это вызывается:

std::thread loader(loadfile, fname, fbuffer, fsize); 
loader.detach(); 

В какой-то момент что-то в моей программе хочет прекратить чтение этого файла и запросить другой файл. Проблема в том, что к моменту удаления указателя fbuffer поток loader все еще продолжается, и я получаю условие гонки, которое бросает исключение.

Как я могу убить эту ветку? Моя идея состояла в том, чтобы проверить наличие требуемого fbuffer и, возможно, разделить fread небольших порций:

void loadfile(char *fname, char *fbuffer, long fsize) 
{ 
    FILE *fp = fopen(fname, "rb"); 
    long ch = 0; 
    while (ch += 256 < fsize) 
    { 
    if (fbuffer == NULL) return; 
    fread(fbuffer + ch, 1, 256, fp); 
    } 
    fclose(fp); 
} 

Будет ли это замедляет чтение файла? У вас есть идея?

+0

Просто пропустите поток, вам не обязательно использовать результат в конце концов. –

+0

Вы не можете убить отдельный протектор: http://en.cppreference.com/w/cpp/thread/thread/detach –

+0

@UlrichEckhardt, если я удалю 'fbuffer', когда поток запущен, программа выйдет из строя. –

ответ

5

Вы должны избегать любого возможного повреждения. Это приводит к тому, что зло происходит, например, ресурсы, оставшиеся в состоянии постоянной блокировки.

Поток должен иметь ссылку на флаг, значение которого можно установить из другого места, чтобы сообщить потоку добровольно выйти.

Вы не можете использовать буфер для этой цели; если один поток удаляет память буфера, в то время как другой пишет, очень злые вещи будут происходить. (Повреждение памяти.) Итак, передайте ссылку на логический флаг.

Конечно, для того, чтобы поток мог периодически проверять флаг, он должен иметь небольшие куски работы, поэтому разделение вашего fread s на небольшие куски было хорошей идеей.

256 байтов может быть немного слишком маленьким; определенно используйте 4k или более, возможно, даже 64k.

0

Killing темы, как правило, не путь - делать это may lead to leaked resources, critical sections you cannot exit и inconsistent program state.

Ваше представление почти безошибочный, но вам нужен способ, чтобы сигнализировать нить для завершения операции. Вы можете использовать логическое разделяемое между вашим потоком и остальной частью вашего кода, которое ваш поток читает после каждого чтения, и после его установки перестает считывать в буфер, очищает файлы и выводит их чисто.

Еще одно замечание: обработка указателей с собственными семантиками сама по себе в большинстве случаев неодобрилась в современном C++ - если у вас нет веских оснований, я бы рекомендовал использовать классы stl fstream и string ,

0

Вам нужна правильная синхронизация потоков. Комментарии об утечках ресурсов и предложение @Mike Nakis о том, что вывод потока происходит добровольно, путем установки логического значения: почти правильно (ну, это правильный, но не полный). Тебе нужно идти еще дальше.

Вы должны убедиться в том, что нить загрузчика выходит сама по себе, вы должны также убедиться, что она вышла, прежде чем удалять буфер, на который он пишет. Или, по крайней мере, вы должны убедиться, что он никогда не трогает этот буфер каким-либо образом после его удаления.Проверка указателя на null-ness не работает по двум причинам. Во-первых, это не работает, так как вы смотрите на копию исходного указателя (вам нужно будет использовать указатель-указатель или ссылку). Во-вторых, и что еще более важно, даже если проверка работала, между оператором if и fread существует условие гонки. Другими словами, нет способа гарантировать, что вы не освобождаете буфер, а fread обращается к нему (независимо от того, насколько малы вы делаете свои куски).

В самом минимуме, вы два логических нужду флагов, но предпочтительно вы бы использовать надлежащих примитив синхронизации, такие как переменная состоянию, чтобы уведомить основной поток (так что вы не должны вращаться в ожидании загрузчика выход, но может блокировать).

Правильный способ работы будет:

  1. Уведомлять погрузчик нить
  2. Подождите Загрузчик нить сигнализировать меня (блок на конд вар)
  3. Loader нить поднимает уведомление, устанавливает условие переменной и никогда не прикасается к буфере затем, а затем выходит
  4. Резюме (удаление буфера, выделение нового буфера и т. д.)

Если вы не настаиваете на отсоединении нити загрузчика, вместо этого вы можете просто join после того, как он попросит его выйти (так что вам не понадобится cond var).

Смежные вопросы