linux_thread

目录:

原始引用地址: 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);

线程终止

终止某个线程而不终止整个进程,有三种方法:

  1. 从线程主函数return。这种方法对主控线程不适用,从main函数return相当于调用exit。

  2. 一个线程可以调用pthread_cancel终止同一进程中的另一个线程。

  3. 线程可以调用pthread_exit终止自己。

在某些时候需要在外部及时的停止线程,这时候就需要pthread_cancel了,看下面的例子

pthread_cleanup_push()/pthread_cleanup_pop()的详解

pthread_cancel 完美退出线程

pthread_cancel为何无法取消掉一个线程

但是,如果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;
}

给线程设置名字

linux 下设置线程名字

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下查看线程状态

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

结语:

一般使用,其库函数提供的功能已足够了,如果频繁的创建和释放线程可以使用线程池

首页