enabled or disabled, and cancellation type is said to be deferred or asynchronous.

By default, cancellation is deferred, and can occur only at specific points in the program that check whether the thread has been requested to terminate, called cancellation points. Most functions that can wait for an unbounded time should be deferred cancellation points. Deferred cancellation points include waiting on a condition variable, reading or writing a file, and other functions where the thread may be blocked for a substantial period of time. There is also a special function called pthread_testcancel that is nothing but a deferred cancellation point. It will return immediately if the thread hasn't been asked to terminate, which allows you to turn any of your functions into cancellation points.

Some systems provide a function to terminate a thread immediately. Although that sounds useful, it is difficult to use such a function safely. In fact, it is nearly impossible in a normal modular programming environment. If a thread is terminated with a mutex locked, for example, the next thread trying to lock that mutex will be stuck waiting forever.

It might seem that the thread system could automatically release the mutex; but most of the time that's no help. Threads lock mutexes because they're modifying shared data. No other thread can know what data has been modified or what the thread was trying to change, which makes it difficult to fix the data. Now the program is broken. When the mutex is left locked, you can usually tell that something's broken because one or more threads will hang waiting for the mutex.

The only way to recover from terminating a thread with a locked mutex is for the application to be able to analyze all shared data and repair it to achieve a consistent and correct state. That is not impossible, and it is worth substantial effort when an application must be fail-safe. However, it is generally not practical for anything but an embedded system where the application designers control every bit of shared state in the process. You would have to rebuild not only your own program or library state, but also the state affected by any library functions that might be called by the thread (for example, the ANSI C library).

To cancel a thread, you need the thread's identifier, the pthread_t value returned to the creator by pthread_create or returned to the thread itself by pthread_self. Cancelling a thread is asynchronous — that is, when the call to pthread_cancel returns, the thread has not necessarily been canceled, it may have only been notified that a cancel request is pending against it. If you need to know when the thread has actually terminated, you must join with it by calling pthread_join after cancelling it.

If the thread had asynchronous cancelability type set, or when the thread next reaches a deferred cancellation point, the cancel request will be delivered by the system. When that happens, the system will set the thread's cancelability type to PTHREAD_CANCEL_DEFERRED and the Cancelability State to PTHREAD_CANCEL_DISABLE.

That is, the thread can clean up and terminate without having to worry about being canceled again.

When a function that is a cancellation point detects a pending cancel request, the function does not return to the caller. The active cleanup handlers will be called, ifthere are any, and the thread will terminate. There is no way to 'handle' cancellation and continue execution — the thread must either defer cancellation entirely or terminate. This is analogous to C++ object destructors, rather than C++ exceptions — the object is allowed to clean up after itself, but it is not allowed to avoid destruction.

The following program, called cancel.c, shows how to write a thread that responds 'reasonably quickly' to deferred cancellation, by calling pthread_ testcancel within a loop.

11-19 The thread function thread_routine loops indefinitely, until canceled, testing periodically for a pending cancellation request. It minimizes the overhead of calling pthread_testcancel by doing so only every 1000 iterations (line 17).

27-35 On a Solaris system, set the thread concurrency level to 2, by calling thr_ setconcurrency. Without the call to thr_setconcurrency, this program will hang on Solaris because thread_routine is 'compute bound' and will not block. The main program would never have another chance to run once thread_routine started, and could not call pthread_cancel.

36-54 The main program creates a thread running thread_routine, sleeps for two seconds, and then cancels the thread. It joins with the thread, and checks the return value, which should be PTHREAD_CANCELED to indicate that it was canceled, rather than terminated normally.

¦ cancel.c

1 #include <pthread.h>

2 #include 'errors.h' 3

4 static int counter;

5

6 /*

7 * Loop until canceled. The thread can be canceled only

8 * when it calls pthread_testcancel, which it does each 1000

9 * iterations. 10 */

11 void *thread_routine (void *arg)

12 {

13 DPRINTF (('thread_routine starting '));

14 for (counter = 0; ; counter++)

15 if ((counter % 1000) == 0) {

16 DPRINTF (('calling testcancel '));

17 pthread_testcancel ();

18 }

19 }

20

21 int main (int argc, char *argv[])

22 {

23 pthread_t thread_id;

24 void *result;

25 int status;

26

27 #ifdef sun

28 /*

29 * On Solaris 2.5, threads are not timesliced. To ensure

30 * that our two threads can run concurrently, we need to

31 * increase the concurrency level to 2.

32 */

33 DPRINTF (('Setting concurrency level to 2 '));

34 thr_setconcurrency (2);

35 #endif

36 status = pthread_create (

37 &thread_id, NULL, thread_routine, NULL);

38 if (status != 0)

39 err_abort (status, 'Create thread');

40 sleep (2);

41

42 DPRINTF (('calling cancel '));

43 status = pthread_cancel (thread_id);

44 if (status != 0)

45 err_abort (status, 'Cancel thread');

46

47 DPRINTF (('calling join '));

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

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

Отметить Добавить цитату