场景
1.大多数情况下我们都从线程池获取工作线程执行任务,线程池的线程缺点就是不能使用线程的取消特性. 因为线程取消(停止)就是终止线程, 终止后的线程不能再次重用.
2.在有线程池的情况下, pthread_cancel 目前发现只能用在程序退出时终止所有线程. 至于线程池的取消操作, 有很多种方法, 比如使用Thread-specific data.(最终还是要判断某个变量)
3.如果没有线程池, 那么可以用pthread_cancel来终止线程.
4.一般线程取消用在快速响应界面请求或者释放资源的情形.
说明
1.pthread线程取消类型有两种, Deferred(延迟)和 Asynchronous(异步). Defrerred:在下一个取消点函数调用时终止线程,取消点函数包括
pthread_testcancel
pthread_cond_wait
pthread_cond_timewait
pthread_join
...
在Linux上, 只要有关信号响应的函数都可作为取消点; Windows上还是老老实实用pthread相关函数吧. 有一点要注意, pthread_mutex_lock并不是取消检查点. 它是安全的取消类型, 因为取消点是可以预估的.
Asynchronous: 异步取消, 它可以在任何时候终止线程. 所以它适合在非锁环境和非复杂创建销毁内存的环境, 当然退出程序除外. 它不是安全的取消类型, 如果要使用, 需要编码确保上下文安全.
2.以下有个例子, 比较全面的说明了取消的相关函数, 该例子使用pthread-win32和vs2010.
int pthread_cancel(pthread_t thread);
int pthread_setcancelstate (int state,int *oldstate);
int pthread_setcanceltype (int type,int *oldtype);
int pthread_testcancel (void);
void pthread_cleanup_push(void (*routine)(void *),void *arg);
void pthread_cleanup_pop(int execute);
例子
// test-pthread.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <Windows.h>
#include <iostream>
#include <memory>
#include "pthread.h"
class A
{
public:
~A(){
std::cout << "~A: " << i << std::endl;
}
int i;
};
#define SIZE 10 /* array size */
static int matrixa[SIZE][SIZE];
static int matrixb[SIZE][SIZE];
static int matrixc[SIZE][SIZE];
// 摘录部分 cancel_async.c
static void BusyWorkUnControl(int& index){
int i, j, k, value = 1;
/*
* Initialize the matrices to something arbitrary.
*/
for (i = 0; i < SIZE; i++)
for (j = 0; j < SIZE; j++) {
matrixa[i][j] = i;
matrixb[i][j] = j;
}
while (1) {
++index;
/*
* Compute the matrix product of matrixa and matrixb.
*/
for (i = 0; i < SIZE; i++)
for (j = 0; j < SIZE; j++) {
matrixc[i][j] = 0;
for (k = 0; k < SIZE; k++)
matrixc[i][j] += matrixa[i][k] * matrixb[k][j];
}
/*
* Copy the result (matrixc) into matrixa to start again
*/
for (i = 0; i < SIZE; i++)
for (j = 0; j < SIZE; j++)
matrixa[i][j] = matrixc[i][j];
}
}
static void* AsyncCancelThread(void* data){
int type = 0;
// 设置异步取消
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,&type);
// 密集计算性, 比如调用第3方接口或者进行音视频转换.
// 在退出程序时尤其有效,应用在密集运行区间.
// 注意线程不要有mutex加锁操作,因为系统可能会产生不可查询的中断.
int* count = (int*)data;
BusyWorkUnControl(*count);
pthread_setcanceltype(type,&type);
return NULL;
}
static void CleanUp(void* data){
std::cout << "CleanUp" << std::endl;
free(data);
}
static void* DeferredThread(void* data){
char* buf = (char*)malloc(16);
int* count = (int*)data;
// 如果调用了取消操作,清理函数就会在函数结束前调用.
pthread_cleanup_push(CleanUp,buf);
A* a = new A();
std::shared_ptr<A> sp_a(a); // 使用shared_ptr来释放资源.
int state = 0, status = 0;
for(; (*count)< 1000; ++(*count)){
Sleep(100);
a->i = *count;
if(a->i % 90 == 0){
// 延迟取消,如果判断任务快完成,可以重置取消状态.
// 这里我们判断完成到达90时,只要到达100,即可完成第一阶段,所以设置Disable状态,让达到 100.
// 因为如果不设置状态,如果遇到取消点,一样会返回.
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,&state);
}else if(a->i % 100 == 0){
// 还原状态.
pthread_setcancelstate(state,&state);
pthread_testcancel();
}else{
//pthread_testcancel();
}
}
// 非零: 表示需要调用清理函数.
pthread_cleanup_pop(1);
// 取消后以下基本不会执行
std::cout << "DeferredThread: " << *count << std::endl;
*count = 0;
return NULL;
}
void TestDeferredCancel(){
int count = 1;
pthread_t t1;
pthread_create(&t1,NULL,DeferredThread,&count);
Sleep(2000);
// 默认情况下,取消状态是延期的deferred
pthread_cancel(t1);
void* result = NULL;
pthread_join(t1,&result);
if(result == PTHREAD_CANCELED){
std::cout << "Thread Cancel: " << count << std::endl;
}else{
std::cout << "Thread Not Cancel: " << count << std::endl;
}
}
void TestAsyncCancel(){
int count = 1;
pthread_t t1;
pthread_create(&t1,NULL,AsyncCancelThread,&count);
Sleep(2000);
// 默认情况下,取消状态是延期的deferred
pthread_cancel(t1);
void* result = NULL;
pthread_join(t1,&result);
if(result == PTHREAD_CANCELED){
std::cout << "Thread Cancel: " << count << std::endl;
}else{
std::cout << "Thread Not Cancel: " << count << std::endl;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
std::cout << "TestDeferredCancel Begin" << std::endl;
TestDeferredCancel();
std::cout << "TestDeferredCancel End" << std::endl;
std::cout << "TestAsyncCancel Begin" << std::endl;
TestAsyncCancel();
std::cout << "TestAsyncCancel End" << std::endl;
system("pause");
return 0;
}
输出:
TestDeferredCancel Begin
CleanUp
Thread Cancel: 100
TestDeferredCancel End
TestAsyncCancel Begin
Thread Cancel: 485616
TestAsyncCancel End
参考
pthread_cancel
pthread_cleanup_push
Programming With POSIX Threads
pthreads-cancel-blocking-thread
pthreads-win32