Linux Thread 最基本用法

关于POSIX thread的最基本用法

要用到线程,但对线程一直不怎么懂,看了些资料作了两个例子和总结,不对的地方恳请各位指正。

1.基本函数

pthread_create,pthread_detach,pthread_join,pthread_exit,pthread_self

具体的意义和参数看man或者书吧,其他的函数还不会用。


pthread_create
pthread_create是类Unix操作系统(Unix、Linux、Mac OS X等)的创建线程的函数。
头文件
#include <pthread.h>


函数声明
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine) (void *), void *arg);
编译链接参数
-pthread


返回值
若线程创建成功,则返回0。若线程创建失败,则返回出错编号,并且*thread中的内容是未定义的。
返回成功时,由tidp指向的内存单元被设置为新创建线程的线程ID。attr参数用于指定各种不同的线程属性。新创建的线程从start_rtn函数的地址开始运行,该函数只有一个万能指针参数arg,如果需要向start_rtn函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg的参数传入。
linux下用C语言开发多线程程序,Linux系统下的多线程遵循POSIX线程接口,称为pthread。
由 restrict 修饰的指针是最初唯一对指针所指向的对象进行存取的方法,仅当第二个指针基于第一个时,才能对对象进行存取。对对象的存取都限定于基于由 restrict 修饰的指针表达式中。 由 restrict 修饰的指针主要用于函数形参,或指向由 malloc() 分配的内存空间。restrict 数据类型不改变程序的语义。 编译器能通过作出 restrict 修饰的指针是存取对象的唯一方法的假设,更好地优化某些类型的例程。


参数
第一个参数为指向线程标识符的指针。
第二个参数用来设置线程属性。
第三个参数是线程运行函数的起始地址。
最后一个参数是运行函数的参数。


注意事项
因为pthread并非Linux系统的默认库,而是POSIX线程库。在Linux中将其作为一个库来使用,因此加上 -lpthread(或-pthread)以显式链接该库。函数在执行错误时的错误信息将作为返回值返回,并不修改系统全局变量errno,当然也无法使用perror()打印错误信息。


函数定义: int pthread_join(pthread_t thread, void **retval);
描述 :
pthread_join()函数,以阻塞的方式等待thread指定的线程结束。当函数返回时,被等待线程的资源被收回。如果进程已经结束,那么该函数会立即返回。并且thread指定的线程必须是joinable的。
参数 :
thread: 线程标识符,即线程ID,标识唯一线程。
retval: 用户定义的指针,用来存储被等待线程的返回值。
返回值 : 0代表成功。 失败,返回的则是错误号。


pthread_detach

创建一个线程默认的状态是joinable, 如果一个线程结束运行但没有被join,则它的状态类似于进程中的Zombie Process,即还有一部分资源没有被回收(退出状态码),所以创建线程者应该调用pthread_join来等待线程运行结束,并可得到线程的退出代码,回收其资源(类似于wait,waitpid)
但是调用pthread_join(pthread_id)后,如果该线程没有运行结束,调用者会被阻塞,在有些情况下我们并不希望如此,比如在Web服务器中当主线程为每个新来的链接创建一个子线程进行处理的时候,主线程并不希望因为调用pthread_join而阻塞(因为还要继续处理之后到来的链接),这时可以在子线程中加入代码
pthread_detach(pthread_self())
或者父线程调用
pthread_detach(thread_id)(非阻塞,可立即返回)
这将该子线程的状态设置为detached,则该线程运行结束后会自动释放所有资源。


pthread_t pthread_self(void);
函数作用:获得线程自身的ID。pthread_t的类型为unsigned long int,所以在打印的时候要使用%lu方式,否则将产生神奇的结果。


2.基本用法

程序1

程序功能:main产生一个线程,线程根据main传来的参数产生几个60-100的随机数;main待线程退出后退出。

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

int myRand(void* cnt)
{
    int min = 60;
    int max = 100;
    int randCnt = *((int *)cnt);
    int i = 0;
    pthread_t thread_id = pthread_self();
    
    
    srand((unsigned int)time(NULL));
    for(; i < randCnt; i ++){
        
        printf("thread_id = %d rand()%02d = %d\n",
               thread_id, i, min + rand() % (max - min));
        sleep(1);
    }
    //return 11;
    pthread_exit((void*)11);
}

int main(int argc, char* argv[])
{
    pthread_t tid;
    void* result;
    int reqRandCnt = 5;
    
    if(pthread_create(&tid, NULL, (void *)myRand, (void *)&reqRandCnt) == 0){
        printf("myRand thread create OK!\n");
        //pthread_detach(tid);
    }
    if(pthread_join(tid, &result) == 0){
        printf("thread tid = %d, result = %d\n", tid, (int)result);
    }
    return 0;
    //pthread_exit((void*)22);
}


一次运行结果:

thread_id = 1082367168 rand()00 = 95

myRand thread create OK!

thread_id = 1082367168 rand()01 = 71

thread_id = 1082367168 rand()02 = 63

thread_id = 1082367168 rand()03 = 81

thread_id = 1082367168 rand()04 = 66

thread tid = 1082367168, result = 11

几点说明

1) main用pthread_create产生一个线程,最主要的是后第三第四个参数,第三参数是完成线程的函数,第四参数是传给线程的参数。这里传的是一个整数,如果线程完成的功能改为根据main需要产生x个值在[m, n)的随机数,可以将三个参数定义一个构造体传给线程。

2) main用pthread_join等待线程完成退出后再退出,类似进程的wait函数。值得注意的是该函数的第二个参数,它指向线程的返回值,用了一个二级指针,不怎么明白。

另外pthread_join与pthread_detach只能用其一。详见参考资料。

 

<参考资料语>

一般情况下,进程中各个线程的运行都是相互独立的,线程的终止并不会通知,也不会影响其他线程,终止的线程所占用的资源也并不会随着线程的终止而得到释 放。正如进程之间可以用wait()系统调用来同步终止并释放资源一样,线程之间也有类似机制,那就是pthread_join()函数

pthread_join()的调用者将挂起并等待th线程终止,retval是pthread_exit()调用者线程(线程ID为th)的返回值,如 果thread_return不为NULL,则*thread_return=retval。需要注意的是一个线程仅允许唯一的一个线程使用pthread_join()等待它的终止,并且被等待的线程应该处于可join状态,即非DETACHED状态

如果进程中的某个线程执行了pthread_detach(th),则th线程将处于DETACHED状态,这使得th线程在结束运行时自行释放所占用的 内存资源,同时也无法由pthread_join()同步,pthread_detach()执行之后,对th请求pthread_join()将返回错误

一个可join的线程所占用的内存仅当有线程对其执行了pthread_join()后才会释放,因此为了避免内存泄漏,所有线程的终止,要么已设为DETACHED,要么就需要使用pthread_join()来回收

3) 主线程用pthread_exit还是return

用pthread_exit只会使主线程自身退出,产生的子线程继续执行;用return则所有线程退出。

综合以上要想让子线程总能完整执行(不会中途退出),一种方法是在主线程中调用pthread_join对其等待,即pthread_create/pthread_join/pthread_exit或return;一种方法是在主线程退出时使用pthread_exit,这样子线程能继续执行,即pthread_create/pthread_detach/pthread_exit;还有一种是pthread_create/pthread_detach/return,这时就要保证主线程不能退出,至少是子线程完成前不能退出。现在的项目中用的就是第三种方法,主线程是一个死循环,子线程有的是死循环有的不是。

<参考资料语>

理论上说,pthread_exit()和线程宿体函数退出的功能是相同的,函数结束时会在内部自动调用pthread_exit()来清理线程相关的资源。但实际上二者由于编译器的处理有很大的不同。

在进程主函数(main())中调用pthread_exit(),只会使主函数所在的线程(可以说是进程的主线程)退出;而如果是return,编译器将使其调用进程退出的代码(如_exit()),从而导致进程及其所有线程结束运行。

你可能感兴趣的:(Linux Thread 最基本用法)