线程--线程基本操作

 

1.创建线程

#include <pthread.h>

int pthread_create(pthread_t  * tidp,const pthread_attr_t * attr,void *  (* start_rtn)(void * ),void * args);

 

函数返回时,tidp指向内核分配给线程的ID。attr表示线程的属性,如果不想指定该属性,可以设置为NULL。

第三个参数指定线程开始执行的函数。第四个参数是传递给第三个参数所指定的函数的参数。

 

得到线程的id

#include<pthread.h>

pthread_t pthread_self();

 

判断两个线程的id是否相同:

#include <pthread.h>

int pthread_equal(pthread_t tid1,pthread_t tid2);

 

 

//printtid.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>

 
void * thfn(void * arg)
{
    pid_t pid; 
    pthread_t tid;
     
    pid = getpid(); 
    tid = pthread_self(); 
     
    printf("the new thread: pid is: %u, tid is: %u/n", (unsigned int)pid, (unsigned int)tid);
 
    return NULL;
}
 
int main(void)
{
    pid_t pid;
    int err;
    pthread_t ntid, mtid;
     
    pid = getpid();
    mtid = pthread_self();     //得到主线程的线程ID                         
    err = pthread_create(&ntid, NULL, thfn, NULL);//创建线程,如果出错,err保存错误号      
    if(err != 0){
        printf("can't create thread %s/n", strerror(err));
        exit(1);     
    }
         
    sleep(1);
     
    printf("the main thread: pid is: %u, tid is: %u/n", (unsigned int)pid, (unsigned int)mtid);
         
    return 0;
}

 

运行:

gcc printtid.c -o printtid -lpthread由于pthread库不是Linux系统默认的库,连接时需要使用库libpthread.a,所以在使用pthread_create创建线程时,在编译中要加-lpthread参数,否则会提示找不到pthread_create函数

 ./printtid
the new thread: pid is: 4245, tid is: 3084643216
the main thread: pid is: 4245, tid is: 3084646080

 

 

2.向线程体传递函数

如果需要传递多个参数则需要将所有参数组织在一个结构体内,再将这个结构体的地址作为参数传给新线程。

 

//mularg.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
 
typedef struct arg_struct ARG;
 
struct arg_struct{
    char arg1[10];
    int arg2;
    float arg3;
};
 
void * thfn(void * arg)
{
    ARG * p = (ARG *)arg;              
    printf("arg1 is : %s, arg2 is : %d, arg3 is : %f/n", p->arg1, p->arg2, p->arg3); 
 
    return NULL;
}
 
int main(int argc, char *argv[ ])
{
    pthread_t tid;
    ARG arg;                                              
    int err;
   
        //对三个参数的赋值,要在创建线程之前
    strcpy(arg.arg1, argv[1]);
    arg.arg2 = atoi(argv[2]);
    arg.arg3 = atof(argv[3]);
     
    err = pthread_create(&tid, NULL, thfn, (void *)&arg);//创建线程,并看是怎么传的参数                  
    if(err != 0){
        printf("can¡¯t create thread %s/n", strerror(err));
        exit(1);     
    }
     
    return 0;
}

 

 

 

运行:

./mularg linux 5 2.3
arg1 is : linux, arg2 is : 5, arg3 is : 2.300000

 

 

 

3.线程访问资源的限制

新线程和进程(主线程)公用文件描述符,文件对象和数据段,以及堆和栈。因此进程中的地址空间对于它的任意一个线程都是开放的。

 

//thread_access.c

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>

typedef struct arg_struct{
    int * heap;
    int * stack;
} ARG;

FILE * fp=NULL;

void * thfn(void * arg)
{
    ARG * p;
    p=(ARG *)arg;

    (*p->heap)++;
    (*p->stack)++;

    fprintf(fp,"new thread heap:%d stack: %d/n",*(p->heap),*(p->stack));
    printf("the new thread done./n");

    return NULL;
}

int main()
{
    pthread_t tid,tid2;
    ARG arg;
    int * heap;
    int stack;
    int err;

    heap=(int *)malloc(sizeof(int));//得到堆数据
    if(heap==NULL){
        perror("fail to malloc");
        exit(1);
    }

    *heap=2;
    stack=3;

    arg.heap=heap;
    arg.stack=&stack;

    if((fp=fopen("test.txt","wb"))==NULL){
        perror("fail to open");
        exit(1);
    }

    err=pthread_create(&tid,NULL,thfn,(void *)&arg);
    if(err!=0){
        printf("can't create thread %s/n",strerror(err));
        exit(1);
    }

    sleep(10);//保证新线程先运行

    (*heap)++;
    stack++;

    fprintf(fp,"main thread:heap:%d  stack: %d/n",*heap,stack);
    printf("the main thread done./n");

    fclose(fp);

    free(heap);

    return 0;
}

 

 

运行:

alei@alei-desktop:~/linux/code/15$gcc thread_access.c -o thread_access -lpthread

alei@alei-desktop:~/linux/code/15$ ./thread_access
the new thread done.
the main thread done.
alei@alei-desktop:~/linux/code/15$ cat test.txt
new thread heap:3 stack: 4
main thread:heap:4  stack: 5

 

现在也可解释,为什么线程系列处理函数中不设置errno变量,而是采用返回出错号的原因了。由于线程可以随意的访问进程的环境变量,所以多个线程出错时,errno变量的值将被多次覆盖,进程检查到的只是最后一个出错线程的出错原因。

 

 

4.终止线程

线程的退出有三种方式:

a 线程体函数结束

b 线程被另一个线程所取消

c 线程自行退出

 

Linux环境下使用pthread_exit函数终止线程。

 

#include<pthread.h>

void pthread_exit(void * rval_ptr);

 

pthread_exit函数的参数是一个指向任意类型的指针,该指针指向的区域存储退出信息,该信息类似与传递给新线程的参数,可以将多个信息组织成一个结构体。

一个线程的结束信息可以有两种,一种是线程体函数返回的指针所指向的区域,另一种就是pthread_exit函数所指向的区域。第一种

方法可以得到线程体函数的返回值,而第二种方法得到pthread_exit函数所设置的退出信息。

 

 

Linux环境下使用pthread_join函数访问指定的线程的结束信息

 

#include <pthread.h>

int pthread_join(pthread_t tid,void ** rval_ptr);

 

 

如果该线程尚在运行中,pthread_join函数会阻塞。如果指定线程的线程id和调用线程不属于同一个进程,则pthread_join函数出错返回。

如果调用函数对指定线程的退出信息并不关心,则可以将rval_ptr参数设置为NULL。这是调用线程仅仅等待指定线程结束执行,而不获得线程结束信息。

 

eg:

//thread_exit.c

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>

//第一个线程,线程体函数返回
void * tfn1(void * arg)
{
    printf("the first/n");
    return (void *)1;     
}
//调用pthread_exit函数退出
void * tfn2(void * arg)
{
    printf("the second/n");
    pthread_exit((void *) 3);         
    printf("should not be here/n");
}
//休眠5秒中退出
void *tfn3(void *arg)
{
    printf("the third, sleep 5 secconds/n");
    sleep(5); 
    return NULL;
}
 
int main(void)
{
    pthread_t tid1, tid2, tid3;
    void * res; 
    int err; 
     
    err = pthread_create(&tid1, NULL, tfn1, NULL); 
    if(err != 0){
        printf("can¡¯t create thread %s/n", strerror(err));
        exit(1);     
    }
     
    err = pthread_join(tid1, &res); //得到线程退出信息,tfn1函数的返回值
    if(err != 0){
        printf("can¡¯t join thread %s/n", strerror(err));
        exit(1);     
    }
 
    printf("result from thd1: %d/n", (unsigned int)(res)); 
     
    err = pthread_create(&tid2, NULL, tfn2, NULL); 
    if(err != 0){
        printf("can¡¯t create thread %s/n", strerror(err));
        exit(1);     
    }
     
    err = pthread_join(tid2, &res);//得到线程的退出信息,pthread_exit函数的参数所设置的值
    if(err != 0){
        printf("can¡¯t join thread %s/n", strerror(err));
        exit(1);     
    }
 
    printf("result from thd2: %d/n", (unsigned int)(res));
 
    err = pthread_create(&tid3, NULL, tfn3, NULL); 
    if(err != 0){
        printf("can¡¯t create thread %s/n", strerror(err));
        exit(1);     
    }
     
    err = pthread_join(tid3, NULL); //不关心退出信息,只等待第三个线程退出
    if(err != 0){
        printf("can¡¯t join thread %s/n", strerror(err));
        exit(1);     
    }
    printf("the third thread has done/n");
     
    return 0;
}

 

运行:

alei@alei-desktop:~/linux/code/15$ gcc -lpthread thread_exit.c -o thread_exit
alei@alei-desktop:~/linux/code/15$ ./thread_exit
the first
result from thd1: 1
the second
result from thd2: 3
the third, sleep 5 secconds
the third thread has done

 

5.正确得到线程退出信息

在线程结束运行后,Linux内核中保存的只是存储退出信息内存区域的地址,因此,在线程结束运行后,其保存退出信息的内存区域要仍然有效,所以不能存储在局部变量中,而应该使用动态分配的内存或者全局变量。

eg;

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
 
struct a{
    int b;
    int c;     
}r3; //定义全局变量
 
/* 第一个线程使用局部变量存储退出信息结构 */
void * tfn1(void * arg)
{
    struct a r1;     
    printf("the first one/n "); 
     
    r1.b = 10; /* 设置退出信息 */
    r1.c = 11;
     
    return (void *)(&r1); /* 返回退出信息的首地址 */
}
 
/* 第二个线程使用动态分配内存的方法存储退出信息结构 */
void * tfn2(void * arg)
{
    struct a * r2; 
     
    printf("the second one/n "); 
     
    r2 =(struct a *) malloc(sizeof(struct a));
     
    printf("structure at %x/n", r2);
     
    r2->b = 10; /* 设置退出信息 */
    r2->c = 11;
     
    return (void *)r2; /* 返回退出信息的首地址 */
}
 
/* 第三个线程使用全局变量存储退出信息结构 */
void * tfn3(void * arg)
{
    printf("the third one/n:"); 
     
    r3.b = 10; /* 设置退出信息 */
    r3.c = 11;
     
    return (void *)(&r3); /*返回退出信息的首地址*/
}
 
/* 第四个线程使用main函数中的局部变量存储退出信息结构 */
void * tfn4(void * arg)
{
    struct a *r4 = (struct a *)arg;
 
    printf("the fourth one/n");  
     
    r4->b = 10; /* 设置退出信息 */
    r4->c = 11;
     
    return (void *)(&r4); /*返回退出信息的首地址*/
}
 
int main(void)
{
     pthread_t tid1, tid2, tid3, tid4;
     void * res;
     int err;
          
     
     err = pthread_create(&tid1, NULL, tfn1, NULL); 
    if(err != 0){
        printf("can‘t create thread %s/n", strerror(err));
        exit(1);     
    }
    err = pthread_join(tid1, &res); 
    if(err != 0){
        printf("can¡’t join thread %s/n", strerror(err));
        exit(1);     
    }
    printf("1st result: %d, %d/n", ((struct a *)res)->b, ((struct a *)res)->c); 
     
     
    err = pthread_create(&tid2, NULL, tfn2, NULL);
    if(err != 0){
        printf("can't create thread %s/n", strerror(err));
        exit(1);     
    }
     
    err = pthread_join(tid2, &res); 
    if(err != 0){
        printf("can¡'t join thread %s/n", strerror(err));
        exit(1);     
    }
    printf("2nd result: %d, %d/n", ((struct a *)res)->b, ((struct a *)res)->c); 
    free(res); 
 
     
    err = pthread_create(&tid3, NULL, tfn3, NULL);
    if(err != 0){
        printf("can¡¯t create thread %s/n", strerror(err));
        exit(1);     
    }
     
    err = pthread_join(tid3, &res); 
    if(err != 0){
        printf("can¡¯t join thread %s/n", strerror(err));
        exit(1);     
    }
    printf("3rd result: %d, %d/n", ((struct a *)res)->b, ((struct a *)res)->c); 
 
     
    err = pthread_create(&tid4, NULL, tfn2, NULL);
    if(err != 0){
        printf("can¡¯t create thread %s/n", strerror(err));
        exit(1);     
    }
     
    err = pthread_join(tid4, &res); 
    if(err != 0){
        printf("can¡¯t join thread %s/n", strerror(err));
        exit(1);     
    }
    printf("4th result: %d, %d/n", ((struct a *)res)->b, ((struct a *)res)->c); 
     
     return 0;
}

 

结果:

alei@alei-desktop:~/linux/code/15$ gcc -lpthread thread_exit_status.c -o thread_exit_status
alei@alei-desktop:~/linux/code/15$ ./thread_exit_statusthe first one
 1st result: -1208028572, -1209470008
the second one
 structure at 9795098
2nd result: 10, 11
the third one
:3rd result: 10, 11
the second one
 structure at 9795098
4th result: 10, 11

 

 

可见第一个线程的退出信息是一些无用的值。

 

 

6.取消一个线程的运行

 

#include <pthread.h>

int pthread_cancel(pthread_t tid);

 

调用该函数,相当于被取消的函数调用pthread_exit(PTHREAD_CANCELED);

 

eg:

//cancel.c

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>

 
void * tfn1(void * arg)
{
    printf("new thread/n"); 
    sleep(10); 
}
 
int main(void)
{
    pthread_t tid;
    void * res;
    int err;
     
    err = pthread_create(&tid, NULL, tfn1, NULL); 
    if(err != 0){
        printf("can¡¯t create thread %s/n", strerror(err));
        exit(1);     
    }
     
    err = pthread_cancel(tid); //取消新创建的线程
    if(err != 0){
        printf("can¡¯t cancel thread %s/n", strerror(err));
        exit(1);     
    }
     
    err = pthread_join(tid, &res); //取得线程的退出信息
    if(err != 0){
        printf("can¡¯t join thread %s/n", strerror(err));
        exit(1);     
    }
     
    if(res == PTHREAD_CANCELED)//如果线程被取消,则其退出信息的首地址为PTHREAD_CANCELED常量
        printf("thread %u has been canceled/n", (unsigned int)tid);
    else
        printf("error/n"); //不该出现这种情况
 
    return 0;
}

 

运行:

alei@alei-desktop:~/linux/code/15$ gcc -lpthread cancel.c -o cancel
alei@alei-desktop:~/linux/code/15$ ./cancel
new thread
thread 3085863824 has been canceled

 

7.线程退出函数

 

线程退出时,也可调用清理程序,其函数原型如下:

#include <pthrea.h>

void pthread_cleanup_push(void (*rtn)(void *),void * arg);

void pthread_cleanup_pop(int execute);

 

 

pthread_cleanup_push函数的第一个参数是一个函数指针,指向清理程序。清理程序是一个没有返回值的函数,其参数是一个任意类型的指针。第二个参数是任意类型的指针,该参数实际代表的是清理程序的参数。

pthread_cleanup_pop函数的参数表示是否执行栈顶的清理程序。参数为0时,表示不执行清理程序,但是将栈顶的清理程序记录出栈(删除清理程序记录);参数非0时,表示执行栈顶清理程序,执行之后该记录也会出栈。

pthread_cleanup_push函数会在以下3中情况下执行:

a 调用pthread_exit函数时

b 线程被其他线程取消时

c 使用非0参数调用pthread_cleanup_pop函数

 

 

eg:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
 
void cleanup(void *arg)
{
    printf("No. %d clean-up procdure/n", *((int *)arg)); 
}

//第一个线程,正常退出线程
void *tfn1(void *arg)
{
    int a = 1, b=2;
     
    printf("the first thread /n");
    pthread_cleanup_push(cleanup, &a); //安装清理程序
 
 
 
    pthread_cleanup_push(cleanup, &b); //加载另一个线程退出清理函数
 
    return NULL; 
 
    pthread_cleanup_pop(0); //必须和pthread_cleanup_push函数配对
    pthread_cleanup_pop(0);
 
    return NULL;
}

//第二个线程,调用pthread_exit函数退出
void *tfn2(void *arg)
{
    int a = 1,b=2;
     
    printf("the second thread /n");
    pthread_cleanup_push(cleanup, &a); 
 
  
 
    pthread_cleanup_push(cleanup, &b); 
 
    pthread_exit(NULL); 
 
    pthread_cleanup_pop(0); 
    pthread_cleanup_pop (0);
 
    return NULL;
}

//休眠10秒,等待被取消
void *tfn3(void *arg)
{
    int a = 1,b=2;
     
    printf("the third thread /n");
    pthread_cleanup_push(cleanup, &a); 
     
 
 
    pthread_cleanup_push(cleanup, &b); 
    pthread_cleanup_pop(1); 
 
    printf("ready to sleep/n");
 
    sleep(10); //休眠,等待被取消
 
    pthread_cleanup_pop(0); //执行线程清理函数
 
    return NULL;
}
 
int main(void)
{
    pthread_t tid1, tid2, tid3, tid4;
    int err;
     
    err = pthread_create(&tid1, NULL, tfn1, NULL); 
    if(err != 0){
        printf("can¡¯t create thread %s/n", strerror(err));
        exit(1);     
    }
     
    err = pthread_join(tid1, NULL); 
    if(err != 0){
        printf("can¡¯t join thread %s/n", strerror(err));
        exit(1);     
    }
 
    err = pthread_create(&tid2, NULL, tfn2, NULL); 
    if(err != 0){
        printf("can¡¯t create thread %s/n", strerror(err));
        exit(1);     
    }
     
    err = pthread_join(tid2, NULL); 
    if(err != 0){
        printf("can¡¯t join thread %s/n", strerror(err));
        exit(1);     
    }
 
    err = pthread_create(&tid3, NULL, tfn3, NULL);
    if(err != 0){
        printf("can¡¯t create thread %s/n", strerror(err));
        exit(1);     
    }
     
    err = pthread_cancel(tid3); 
    if(err != 0){
        printf("can¡¯t cancel thread %s/n", strerror(err));
        exit(1);     
    }
 
    err = pthread_join(tid3, NULL); 
    if(err != 0){
        printf("can¡¯t join thread %s/n", strerror(err));
        exit(1);     
    }
     
    return 0;
}

 

运行:

alei@alei-desktop:~/linux/code/15$ gcc -lpthread clean_up.c -o clean_up
alei@alei-desktop:~/linux/code/15$ ./clean_up
the first thread
the second thread
No. 2 clean-up procdure
No. 1 clean-up procdure
the third thread
No. 2 clean-up procdure
ready to sleep
No. 1 clean-up procdure

 

分析:第一个线程没有执行清理程序,说明线程正常退出,并不能触发清理程序,后面两个线程说明了前面说的pthread_cleanup_push函数执行的哪几种情况。

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值