在写篇文章之前,我首先要指出一个自己以前犯的错误,在我的一篇技术类的文章“线程创建---pthread_creat()的问题”中的创建线程函数例程中有exit(0)。在此纠正错误,因为exit(0)直接退出了进程(当然编译是能通过的)。直接用return 0;即可 (这是不懂架势,乱耍花枪的后果)
pthread_exit
void pthread_exit(void *rval_ptr);
rval_ptr是一个无类型指针,与那个传给线程例程函数的单个参数类型一样,作为返回数值,进程中的其他线程可以通过pthread_join()函数来访问到这个指针。
pthread_join
int pthread_join(pthread_t thread,void **rval_ptr);
调用这个函数的调用线程将一直阻塞,直到要等到的那个线程玩完。如果线程执行完了,那么rval_ptr就是pthread_exit()或者return的返回码,如果线程被其它线程取消,那么rval_ptr就是PTHREAD_CANCELED
喏,我再说下,为什么要阻塞到别人的线程玩完?比如说,主线程不想玩了,想退出算了,它不能自顾自地退出啊,它退出了,新线程怎么办?没谁来管,指不定会干出什么事情出来
pthread_cancel
int pthread_cancel(pthread_t tid);
一个线程通过调用这个函数来请求取消同一进程的其他线程,pthread_exit不是有个
参数嘛,这个函数类似于参量为PTHREAD_CANCELED,怪不得pthread_join()会返回这么一个值。但是,这是一个线程终止同一进程中的其他线程。这个应该不多见吧,即使要终止别的线程,也得先通告一声,然后人家的线程自己pthread_exit,这样直接pthread_cancel我觉得太霸道吧,鄙人没用过,不便多说。
好了,介绍完了,看程序:
#include
#include

void *thr_fn1(void *arg)
{
        printf( "thread 1 returning\n");
        return((void *)1);
}

void *thr_fn2(void *arg)
{
        printf( "thread 2 exiting\n");
        pthread_exit((void *)2);
}

int main(void)
{
        int                 err;
        pthread_t     tid1, tid2;
        void                *tret;

        err = pthread_create(&tid1, NULL, thr_fn1, NULL);
             if(err!=0){printf( "pthread_create error:%s\n",strerror(err));}
             //为了结构清晰,后面的出错不再处理
        err = pthread_create(&tid2, NULL, thr_fn2, NULL);

        err = pthread_join(tid1, &tret);
        printf( "thread 1 exit code %d\n", (int)tret);
  
        err = pthread_join(tid2, &tret);
        printf( "thread 2 exit code %d\n", (int)tret);
  
         exit(0);
}
执行结果如下:
 thread 1 returning
 thread 2 exiting
 thread 1 exit code 1
 thread 2 exit code 2
这个结果可是不确定的,第一行和第二行倒一下也是有可能的
线程中栈的问题
我们想想看,如果我们在新线程中创立一个栈,那么这个栈区直属于这个线程,当线程没了,这个栈也就被撤消了,里面的内存也可能被另作它用了,所以,如果返回的是栈的地址,那么将引起不可测的错误。
另外,我发现,如果栈区是结构体变量的话,出错的可能性几乎100%;可是换成普通的变量的话,又基本不出错,具体原因本人尚不知道。不过可以肯定的是,返回的最好不要是线程的栈区变量。
#include
#include

typedef struct foo_t {    int a,b;    }foo;

void *thr_fn1(void *arg)
{
        printf( "thread 1 returning\n");
        return((void *)1);
}

void *thr_fn2(void *arg)
{        
        foo fb; //这句决定变量时全局变量还是栈区变量
        fb.a=1;
        fb.b=2;
        printf( "thread 2 exiting\n");
        pthread_exit((void *)&fb);
}

int main(void)
{
        int                 err;
        pthread_t     tid1, tid2;
        void                *tret;
        foo *temp;

        err = pthread_create(&tid1, NULL, thr_fn1, NULL);
         if(err!=0){printf( "pthread_create error:%s\n",strerror(err));}
                //为了结构清晰,后面的出错不再处理
        err = pthread_create(&tid2, NULL, thr_fn2, NULL);

        err = pthread_join(tid1, &tret);
        printf( "thread 1 exit code %d\n", (int)tret);
  
        err = pthread_join(tid2, &tret);
        temp = (foo*)tret;
        printf( "temp->a=%d\n",temp->a);
        printf( "temp->b=%d\n",temp->b);
  
         exit(0);
}
程序分析:
1. foo fb; 出现在线程函数中,所以是栈区变量,那么执行结果可能是:
       thread 1 returning
       thread 2 exiting
       temp->a=13166208
       temp->b=-1219140504
   充分说明了栈区变量在线程退出后,栈也撤销,栈区变量不可靠的特点
   那怎么办呢?用全局变量,用malloc  (创建的为heap)等,比如本文中把
   foo fb;移到第三行实现全局变量,那么得到的执行结果是:
       thread 1 returning
       thread 2 exiting
       temp->a=1
       temp->b=2
2.对(void*)1 的理解,可能是把这个地址值和数据混淆起来用,或许又在中间进行   了什么处理,反正我不知道,暂且死记硬背:
   return((void *)1);//要返回的值是1,不管什么,前面加个(void*)
   void *tret;
   err = pthread_join(tid1, &tret);
   printf("thread 1 exit code %d\n", (int)tret);
   我们得到的tret我也不认为它是什么指针了,就认为它是一个不知道类型的值1,    然后再强制类型转换一下就可以用了
   简言:当初返回什么,就认为tret是什么,不过认为它是未知类型的
           (这样胡乱理解可以撑一段时间,嘿嘿,希望网友别看)
3.再提醒一下自己关于结构体的基本常识:
         typedef struct foo_t {    int a,b;    }foo;
         foo fb;
     等同于
         struct foo_t {    int a,b;    };
         struct foo_t fb;
      相对而言,第二种方法带来很大的不便,尤其是在定义结构体指针变量的时         候,怎么看怎么别扭
      结构体变量的初始化和数组的初始化差不多:
            在变量声明的时候可以用大括号初始化,中间用','隔开
            不是变量声明的时候,就只能一个一个变量元素去赋值了
4.可能有些人要问,吧foo fb;放到main函数里面,是不是新线程也就可以操作了,错了,因为main不过只是个主线程,在其中定义变量,也还只是栈区变量,那样编译都通不过,因为主线程中拥有的栈区变量只属于它自己啊,还不属于进程啊,所以新线程没法共享得到,所以编译会出错
不过,主线程的变量是可以通过pthread_creat()的最后一个参量传进去的啦