Отмена (p) потока в критическом разделе - PullRequest
4 голосов
/ 20 марта 2009

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

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

Изменение объекта ресурса, вероятно, не является опцией (поставляется третьей стороной), плюс имеет смысл предотвратить одновременный доступ к ресурсу, который нельзя использовать параллельно.

Я экспериментировал с предотвращением отмены потока, используя pthread_setcancelstate, когда секция заблокирована / разблокирована, однако это выглядит немного грязно и не будет окончательным решением для других ситуаций (например, мьютексов, получаемых в результате сбора, и т. Д.).

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

Ответы [ 4 ]

2 голосов
/ 23 февраля 2010

Вы можете использовать pthread_cleanup_push (), чтобы поместить обработчик очистки отмены в стек очистки отмены потоков. Этот обработчик будет отвечать за разблокировку критической секции.

После того, как вы покинете критическую секцию, вы должны вызвать pthread_cleanup_pop (0), чтобы удалить ее.

т.е.

CRIITICAL_SECTION g_section;

void clean_crit_sec( void * )
{
    LeaveCriticalSection( &g_section )
}

void *thrfunc( void * )
{
    EnterCriticalSection( &g_section );
    pthread_cleanup_push( clean_crit_sec, NULL );

    // Do something that may be cancellable

    LeaveCriticalSection( &g_section );
    pthread_cleanup_pop( 0 );
}

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

Вы можете вызвать pthread_cleanup_pop с 1, который будет выполнять ваш код очистки, а не поднимать критический раздел самостоятельно. * 1010 т.е. *

CRIITICAL_SECTION g_section;

void clean_crit_sec( void * )
{
    LeaveCriticalSection( &g_section )
}

void *thrfunc( void * )
{
    EnterCriticalSection( &g_section );
    pthread_cleanup_push( clean_crit_sec, NULL );

    // Do something that may be cancellable

    pthread_cleanup_pop( 1 );  // this will pop the handler and execute it.
}
2 голосов
/ 30 апреля 2009

Отмена темы без помощи приложения (упомянутый флаг) - плохая идея. Просто Google .

На самом деле отмена настолько сложна, что она была опущена в последней версии C ++ 0x. Вы можете выполнить поиск http://www.open -std.org / jtc1 / sc22 / wg21 / docs /apers / 2008 / n2497.html и вообще не найдете упоминаний об отмене. Вот определение предлагаемого класса потока (там вы не найдете отмены):

class thread
{
public:
    // types:
    class id;
    typedef implementation-defined native_handle_type; // See [thread.native]

    // construct/copy/destroy:
    thread();
    template <class F> explicit thread(F f);
    template <class F, class ...Args> thread(F&& f, Args&&... args);
    ~thread();
    thread(const thread&) = delete;
    thread(thread&&);
    thread& operator=(const thread&) = delete;
    thread& operator=(thread&&);

    // members:
    void swap(thread&&);
    bool joinable() const;
    void join();
    void detach();
    id get_id() const;
    native_handle_type native_handle(); // See [thread.native]

    // static members:
    static unsigned hardware_concurrency();
};
0 голосов
/ 07 мая 2009

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

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

Теперь такой дизайн был бы еще хуже для Windows, потому что Windows не подходит для запуска нескольких процессов, однако это не такая плохая идея для Linux.

Конечно, разумный дизайн для ваших резьбовых модулей был бы еще лучше ...

(Лично я предпочитаю вообще не использовать потоки и всегда использовать процессы или неблокирующие конструкции)

0 голосов
/ 20 марта 2009

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

Я думаю, что ваше лучшее решение - и использовать флаг и функциональность pthread_cancel. Когда вы вводите сторонний компонент, отключите обработку отмены (PTHREAD_CANCEL_DISABLE); когда вы вернетесь из него, включите его снова. После повторного включения проверьте флаг:

/* In thread which you want to be able to be canceled: */
int oldstate;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
... call 3rd party component ...
pthread setcancelstate(oldstate, NULL);
if (cancelled_flag) pthread_exit(PTHREAD_CANCELED);

/* In the thread canceling the other one. Note the order of operations
   to avoid race condition: */
cancelled_flag = true;
pthread_cancel(thread_id);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...