2015-06-01 3 views
0

Есть ли способ, чтобы отменить повышение :: нить от другого как в следующем примере ?:Отменять подталкивание нить от другого

boost::thread* thread1(0); 
boost::thread* thread2(0); 

thread2 = new boost::thread([&](){ 
    //some expensive computation that can't be modified 
    if(thread1) 
    thread1->interrupt(); 
}); 

thread1 = new boost::thread([&]() { 
    //some other expensive computation that can't be modified 
    if(thread2) 
    thread2->interrupt(); 
}); 

thread1->join(); 
thread2->join(); 

delete thread1; 
delete thread2; 

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

+2

Канонический ответ: в сотрудничестве с потоком вы можете отменить его, используя любой способ, который он поддерживает. Без сотрудничества с нитью это невозможно. Даже не пытайтесь, это не сработает. (Вам нужно использовать процессы или какой-либо другой механизм. Нити не обеспечивают изоляцию.) –

+0

У вас есть несинхронизированный доступ к 'thread1' и' thread2'. – curiousguy

ответ

4

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

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

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

1

непереносимой решение для POSIX-совместимых системах (например, Linux) будет использовать pthread_cancel(), а затем pthread_join() на native_handle() члена подпиточного потока, который имеет тип pthread_t (опять же, только на POSIX-совместимых систем. Я не могу говорить для других систем, таких как Windows).

Кроме того, вы должны использовать boost::scoped_thread вместо просто boost::thread так что вы можете «переопределить» (не в ОО-смысле) присоединиться к/отсоединять поведение, которые повышают будет делать, когда поток будет уничтожен. Это необходимо, потому что, когда вы вызываете pthread_cancel, то pthread_join на boost::thread объект boost::thread по-прежнему «соединяется» (то есть boost::thread::joinable() возвращает true), и поэтому деструктор будет показывать неопределенное поведение на documentation.

С учетом всего сказанного, если в вашем приложении необходимо решение, зависящее от платформы для отмены таких потоков, я не уверен, что можно использовать многое из использования boost::thread s по сравнению с обычным старым pthread s; все же, я полагаю, для этого может быть и прецедент.

Вот пример кода:

// compilation: g++ -pthread -I/path/to/boost/include -L/path/to/boost/libs -lboost_thread main.cpp 

#include <cstdio> 
#include <pthread.h> 
#include <boost/thread/scoped_thread.hpp> 

typedef struct pthreadCancelAndJoin 
{ 
    void operator()(boost::thread& t) 
    { 
     pthread_t pthreadId = t.native_handle(); 
     int status = pthread_cancel(pthreadId); 
     printf("Cancelled thread %lu: got return value %d\n", pthreadId, status); 

     void* threadExitStatus; 
     status = pthread_join(pthreadId, &threadExitStatus); 
     printf("Joined thread %lu: got return value %d, thread exit status %ld\n", 
       pthreadId, status, (long)threadExitStatus); 
    } 
} pthreadCancelAndJoin; 

void foo() 
{ 
    printf("entering foo\n"); 
    for(int i = 0; i < 2147483647; i++) printf("f"); // here's your 'expensive computation' 
    for(int i = 0; i < 2147483647; i++) printf("a"); 
    printf("foo: done working\n"); // this won't execute 
} 

int main(int argc, char **argv) 
{ 
    boost::scoped_thread<pthreadCancelAndJoin> t1(foo); 
    pthread_t t1_pthread = t1.native_handle(); 

    sleep(1); // give the thread time to get into its 'expensive computation'; 
       // otherwise it'll likely be cancelled right away 

    // now, once main returns and t1's destructor is called, the pthreadCancelAndJoin 
    // functor object will be called, and so the underlying p_thread will be cancelled 
    // and joined 

    return 0; 
} 

pthread_cancel() отменит свой поток, когда он достигает «точки отмены» (если предположить, что cancel type and cancel state находятся в их значения по умолчанию, что в случае boost::thread объектов); см. pthreads man page для списка всех пунктов отмены. Вы заметите, что эти точки отмены включают в себя многие из более общих системных вызовов, как write, read, sleep, send, recv, wait и т.д.

Если ваш «дорогой расчет» включает в себя любой тех (на примере кода printf в конечном итоге вызывает write), он будет отменен.

Прежде всего, Valgrind сообщает об утечке памяти или ошибках памяти с этим решением.


Наконец, заметка о вашем заблуждении в вашем вопросе:

я вычислял стыки будет рассматриваться как точка прерывания ...

join, или любой из функции прерывания boost::thread, если на то пошло, обрабатываются только как точка прерывания для потока, который звонит его. Поскольку ваш основной поток вызывает join(), основной поток - это поток, который испытывает точку прерывания, а не поток, к которому он пытается присоединиться. Например. если вы вызываете thread1.interrupt() в какой-то поток, а затем thread1 звонит thread2.join(), то thread1 - тот, который прерван.

+0

Могу ли я использовать 'boost :: thread :: detach()' перед 'pthread_cancel/pthread_join()' вместо 'boost :: scoped_thread'? Док говорит, что после detach() объект потока является Not-A-Thread one. – Bogdan

+1

@Bogdan вы не можете использовать 'pthread_join()' после 'boost :: thread :: detach()', потому что 'detach()' заставляет поток «отсоединяться» (то есть, вызывая 'pthread_detach()' в капюшон), а на странице руководства ['pthread_detach()') (http://man7.org/linux/man-pages/man3/pthread_detach.3.html) указано, что вы не можете вызвать 'pthread_join()' на отдельной резьбе. Однако я не понимаю, почему вы не сможете использовать 'pthread_cancel()' в отдельном потоке ... ничего, что я могу найти в документации Posix или Boost, что вы не можете. – villapx

+0

Чрезвычайно полезная подсказка! Спасибо! +1 – Bogdan

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