杨坤的分享空间
目录:
原始引用地址: linux therad
time: 2020.7.28 21:27
changelog: 2020.8.3 17:47 pthread_cancel 与锁的问题
现在cpu都是多核了,怎么运用多核性能?程序设计需要运行多任务。实现这些,多线程是最好的选择。
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
终止某个线程而不终止整个进程,有三种方法:
从线程主函数return。这种方法对主控线程不适用,从main函数return相当于调用exit。
一个线程可以调用pthread_cancel终止同一进程中的另一个线程。
线程可以调用pthread_exit终止自己。
在某些时候需要在外部及时的停止线程,这时候就需要pthread_cancel了,看下面的例子
pthread_cleanup_push()/pthread_cleanup_pop()的详解
但是,如果pthread_cancel的线程里面加了锁,没有释放就退出了,就会造成死锁。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <sys/types.h> //gettid
void cleanup(void* buf)
{
printf("call clean\n");
if(buf)
{
free(buf);
}
}
pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER;
/// 如果不使用CANCEL_DISABLE 程序因为无法解锁,几乎都会阻塞无法自动退出
#define USE_CANCEL_DISABLE
void* func(void *param)
{
printf("sub thread:%lld\n", (long long int)syscall(__NR_gettid));
char *buf = (char *)malloc(10);
pthread_cleanup_push(cleanup, buf); // 作线程退出时的清理动作
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); //允许退出线程
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); //设置立即取消
/// 先枷锁
while(1)
{
/// 这段代码可以测试PTHREAD_CANCEL_ENABLE后是否可以正常推出
#ifdef USE_CANCEL_DISABLE
printf("############################ 0 will exit\n");
usleep(21000);
printf("############################ 1 will exit\n");
#endif//USE_CANCEL_DISABLE
#ifdef USE_CANCEL_DISABLE
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); // 先disable cancel, 等待上锁
#endif//USE_CANCEL_DISABLE
printf("0 lock\n");
pthread_mutex_lock(&counter_mutex);
usleep(10000);
printf("1ok\n");
pthread_mutex_unlock(&counter_mutex);
printf("2ok\n");
printf("3ok\n");
#ifdef USE_CANCEL_DISABLE
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); //允许退出线程
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); //设置立即取消
#endif//USE_CANCEL_DISABLE
printf("4ok\n");
}
printf("**************************??????????\n");
pthread_cleanup_pop(0); // 要和pthread_cleanup_push成对出现
// 参数0表示正常退出时不调用,pthread_cancel或异常退出时才调用
return NULL;
}
int main(int argc, char *argv[])
{
pthread_t thrd;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
printf("main thread:%lld\n", (long long int)syscall(__NR_gettid));
if (pthread_create(&thrd, &attr, func, NULL) )
{
perror( "pthread_create error ");
exit(EXIT_FAILURE);
}
sleep(1);
printf("will call cancel\n");
if (!pthread_cancel(thrd) )
{
printf("pthread_cancel OK\n");
}
printf("==================> after call cancel, will lock\n");
pthread_mutex_lock(&counter_mutex);
printf("+++++++++++++++++++can run here????\n");
pthread_mutex_unlock(&counter_mutex);
printf(":::::::::::::::::::::::::::::::::::::::::::::::main thread will exit\n");
return 0;
}
#define SET_STACKSIZE (1*1024*1024 + 200*1024)
/*
* desp : create thread set stack size
* parm : thread fun, args
* return : <0 error, 0 success
*/
int make_thread(pthread_t *tid, void *(fn)(void *), void *arg)
{
int err = 0;
pthread_attr_t attr;
// 初始化线程属性
err = pthread_attr_init(&attr);
if (err != 0)
{
error("ERROR pthreat attr init, ret=%d, errstr:%s\n",
err, strerror(errno));
return err;
}
// 创建分离线程,不用pthread_join, 自己退出后会释放内存
err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
if (err != 0)
{
error("ERROR set statck size, ret=%d, errstr:%s\n",
err, strerror(errno));
goto ERROR;
}
// 设置线程栈空间大小,不使用默认栈空间大小,在多线程情况下可显著降低内存使用
err = pthread_attr_setstacksize(&attr, SET_STACKSIZE);
if (err != 0)
{
error("ERROR set statck size, ret=%d, errstr:%s\n",
err, strerror(errno));
goto ERROR;
}
// 创建内存
err = pthread_create(tid, &attr, fn, arg);
if(err != 0)
{
device_obj_error("ERROR create pthread, ret=%d, errstr:%s\n",
err, strerror(errno));
goto ERROR;
}
err = pthread_attr_destroy(&attr);
if(err != 0)
{
device_obj_error("ERROR set statck size, ret=%d, errstr:%s\n",
err, strerror(errno));
goto ERROR;
}
ERROR:
return err;
}
pthread_create创建线程后,线程名是直接从父进程继承来的,如果对多线程分析,很不方便,可以对不同的线程设置不同的名字
linux下的prctl库自kernel 2.6.9后支持PR_SET_NAME选项,用于设置进程名字
,linux的进程一般使用lwp,所以这个函数可以设置线程名字,api定义如下:
#include <sys/prctl.h>
int prctl(int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5);
PR_SET_NAME (since Linux 2.6.9)
Set the process name for the calling process, using the value in the location pointed to by (char *) arg2. The name
can be up to 16 bytes long, and should be null-terminated if it contains fewer bytes.
查看线程运行状态
linux下使用top和pmap命令查看系统运行状态和进程运行状态
查看/proc/pid/task/tid/stat文件里面的信息,可以查看对应线程的状态。
例如cat /proc/6291/task/6291/status,可以看到线程name, State(运行状态),应用内存等。
测试程序如下所示:
//build gcc t.c -lpthread -Wall
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <sys/types.h> //gettid
void *thread1(void* arg)
{
prctl(PR_SET_NAME,"THREAD1");
printf("thread1 task id:%lld\n", (long long int)syscall(__NR_gettid));
while(1)
{
printf("thread1\n");
sleep(1000);
}
}
void *thread2(void *arg)
{
printf("thread2 task id:%lld\n", (long long int)syscall(__NR_gettid));
while(1)
{
printf("thread2\n");
sleep(1000);
}
}
int main()
{
pthread_t th1,th2;
void* retval;
pthread_create(&th1,NULL,thread1,NULL);
pthread_create(&th2,NULL,thread2,NULL);
printf("main thread:%lld\n", (long long int)pthread_self());
printf("thread1:%lld\n", (long long int)th1);
printf("thread2:%lld\n", (long long int)th2);
pthread_join(th1,&retval);
pthread_join(th2,&retval);
}
// 运行结果
./a.out
main thread:139713583925056
thread1:139713575544576
thread2:139713567151872
main thread:6890
thread1 task id:6891
thread1
thread2 task id:6892
thread2
一般使用,其库函数提供的功能已足够了,如果频繁的创建和释放线程可以使用线程池