2012-05-31 3 views
4

Я делаю серийный IO на linux в C++, используя многопоточность. В настоящее время я использую блокировку чтения. Это оставляет мне никоим образом не остановить поток, который мы рассмотрим в блокировке read(), за исключением того, чтобы принудительно прекратить или прервать поток или использовать что-то вроде отмены pthread. Теперь по всей сети я вижу, как люди кричат ​​на людей, предлагая им прекратить там потоки от блокировки IO. Обычно это касается утечек памяти. Есть ли какая-то магическая утечка памяти, которая может возникнуть из-за прерывания потока, если вы правильно очищаете?Завершение блокировки ввода-вывода в Linux C++

try 
{ 
    while(true) 
    { 
     blocking_read(fd,buffer,512); 
    } 
}catch(interrupt_exception) 
{ 

} 
//clean up, close fd, release heap memory, usual stuff 

Или это моя единственная альтернатива-то вроде ниже или реализуют протокол более высокого уровня, обеспечивающего блокировки чтения получает знак от входа позволяет ему отключился.

try 
{ 
    while(running) 
    { 
     nonblocking_read(fd,buffer,512); 

     if(cancel) 
      running = false; //break return etc 
    } 
} 

//clean up, close fd, release heap memory, usual stuff 

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

Или мне просто не нужно и пусть деструктор убивает нить (я предполагаю, что поток прекращается, когда вы удаляете свой объект, удерживающий поток)? и убирать там? например

class MyClass{ 
    int fd;  
    Thread* myThread; 
    ~MyClass(){ 
     delete myThread; 
     close(fd); 
    } 
}; 

Благодарим за любую помощь!

ответ

6

read() не должно произойти утечка памяти. При использовании как блокирующих, так и неблокирующих считываний код приложения по-прежнему отвечает за управление памятью, предоставленной в качестве аргумента buf. Прерывание read() по сигналу не вызовет исключения, поэтому, если использовать сигналы, вам нужно будет проверить результат и errno.

  • Если read() прерывается сигналом до чтения данных, -1, будет возвращено с егта набором для EINTR.
  • Если read() прерывается сигналом после прочтения некоторых данных, POSIX позволяет либо -1, чтобы быть возвращен с Errno набор для EINTR, или read() вернуть количество байтов уже читать.

Если вы используете pthread_cancel(), тогда будет выбрано исключение. При таком подходе у вас есть следующие варианты:

  • Выполнение очистки путем регистрации функций очистки с помощью pthread_cleanup_push().
  • Выделите динамическую память, сохранив ее в потоковом хранилище с помощью pthread_setspecific().
  • Управление памятью через auto_ptr/unique_ptr.
  • Исключить исключение abi::__forced_unwind, выполнить очистку и ретронировать.

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

Для вашего случая, когда вы используете блокировку чтения, рассмотрите вопрос о том, доступны ли данные через select() с таймаутом и только вызывают read(), если у fd есть данные. Это позволяет периодически проверять, не установлен ли флажок потока, и не позволяет вам обрабатывать сигналы для прерывания потока от read(), так как read() больше не должен блокировать ожидание данных.

Кроме того, поведение, возникающее при удалении объекта потока, зависит от библиотеки потоков. Например, удаление pthread_t или boost::thread не влияет на выполнение связанного потока.

+0

Очень практичный и полезный ответ. Я видел применение этого ответа в проекте, связанном с маршрутизатором, с которым я работал. Спасибо, что дал много вариантов – achoora

0

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

Следующая тестовая программа не показывает ошибок с valgrind, однако, обратите внимание, что она не переносима, чтобы полагаться на генерируемое исключение, когда поток отменен (см. Cancellation and C++ Exceptions) для реализации glibc.

-nick

#include <cstdio> 
#include <pthread.h> 
#include <unistd.h> 

void* thread_func(void*) 
{ 
    try 
    { 
     char buf[2048]; 
     read(1, buf, 2048); 
    } 
    catch(...) 
    { 
     fprintf(stderr, "thread %lu cancelled\n", pthread_self()); 
     throw; 
    } 
    return NULL; 
} 

int main() 
{ 
    pthread_t thread; 
    int res = pthread_create(&thread, NULL, thread_func, NULL); 
    sleep(1); 
    pthread_cancel(thread); 
    void* tres; 
    pthread_join(thread, &tres); 
    return 0; 
} 
+0

Я должен добавить, что неблокирующий IO почти всегда лучше, однако простота pthread_cancel делает его подходящим в некоторых случаях. – mythagel

0

Я бы просто послал сигнал этой теме, вот для чего нужны сигналы. Установите свой глобальный running в false в вашем обработчике и заведите цикл while(running), как во втором фрагменте, и все.

read не просачивается, он считывает в буфер, который вы уже выделили, и о котором вы знаете. Он либо преуспевает, либо сбой (или блокирует, но сигнал разблокирует его). Если это произойдет, посмотрите, что вы можете сделать из данных (это может быть частичное чтение или повреждение или что-то еще), в противном случае повторите, пока running не будет false.

Затем очистите, освободите то, что вы выделили, и изящно выйдите из потока (... или сделайте все, что захотите).

Если я не ошибаюсь, довольно сложная ситуация «до и после получения некоторых данных» ситуация с перезагрузкой системных вызовов не возникает вообще при установке обработчика без предоставления SA_RESTART.
Но даже в этом случае, если возникает, кто заботится - в конце концов, системный вызов может только сбой или преуспеть, и read может всегда возвращать частичные данные даже в отсутствие сигнала, поэтому вы всегда должны быть готовы к это так или иначе. Таким образом, вас действительно интересует только то, собрали ли вы достаточно данных, чтобы сделать что-то полезное, или флаг «running» «волшебным образом» по какой-то причине становится false (эта причина была бы вашим обработчиком сигнала).

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