Linux C语言POSIX线程(学习笔记一)

        进程在Linux中是资源管理的最小单位,而线程是进程执行的最小单位。在操作系统的发展过程中,从进程演化到线程,其最主要的目的就是更好地支持SMP、多核编程以及减少进程调度中的上下文切换开销

        相比之下,多线程模型比多进程模型具有以下优点:

        (1)创建一个新线程的代价要远小于创建一个进程。

        (2)在任务调度方面,线程间的切换速度要快于进程间的切换速度。

        (3)相对于进程间通信,线程间的通信更加简洁和高效

        (4)多线程有利于提高多处理器的效率。

        (5)多线程有利于改善程序设计的结构。

        (6)多线程有利于提高程序对用户的响应速度

1.线程创建函数

        我们可以利用pthread_creat函数来创建一个新的线程,它类似于创建进程的fork函数,函数的原型声明如下:
 

#include <pthread.h>
int pthread_creat(pthread_t *thread,pthread_attr_t *attr, 
    void *(*start_routine)(void *),void *arg),

函数中各个参数的作用如下:
        (1)thread参数

        thread参数是一个指针,当线程创建成功时,他会指向这个新线程的线程ID。通过这个指针可以返回新线程的线程ID,Linux在bits/pthreadtypes.h头文件中定义了pthread_t这个类型,例如:

        typedef unsigned long int pthread_t;

        (2) attr参数

        attr参数用于指定线程的属性,如果传递给它的是一个NULL值,则表示使用的是线程的默认属性。attr的类型是一个结构体指针,该结构体指明了新创建的线程应该具有什么样的属性。

        (3)start_routine函数

        start_routine参数是一个函数指针(即函数地址),指向该线程创建后需要调用的函数,即该线程需要执行的指令序列会被封装在这个函数中,这个被线程调用的函数也称为线程函数。

        线程函数的返回值是一个void型指针,他的参数只有一个,也是一个void型的通用指针,这样就能够向线程函数中传递一个任意类型的实参,并且通过返回一个通用指针来获取线程函数要返回的任意类型的结果,这大大增强了线程函数的可用性。通常线程函数的返回值要根据具体情况使用(type*)形式的强制类型转换变成我们所需要的类ixng指针。对于由fork函数创建的子线程来说,它刚创建时的执行代码与父进程的完全一样,只不过在父子进程中返回的pid值不同而以,但对于创建一个新线程来说,我们必须明确地为它提供一个函数指针,这样新线程才会执行另外一些指令序列。

        (4)arg参数

        arg参数也是一个指针,指向主线程传递给线程函数的参数。如果需要向线程函数传递多个参数,可以考虑将这些参数放到一个结构体中来传递,但是对于传递参数arg的类型是void*,所以参数不可以被提前释放。

        当线程创建成功时,pthread_create函数会返回0;但如果函数返回值不为0则说明创建线程的行为失败,注意pthread_create函数和大多数线程操作函数不同,它在操作失败时返回的不一定就是-1值。线程创建失败的原因可以通过errno变量来获取,例如errno等于EAGAIN时,表示由于线程数目过大,超出了系统限制,所以不能再创建新线程了;如果errno等于EINVAL,通常是由于第二个参数attr给出的线程属性不合法导致的创建失败。

        新线程创建后,将执行由第三个参数start_routine所指向的函数体,而原来的线程也将继续执行。

        在使用pthreas_create函数创建新线程时,常常还会用到pthread.h中声明的其他一些系统调用,常用的有以下三种。

#include <pthread.h>
//获取本线程的线程ID
pthread_t pthreas_self(void);
// 判断两个线程ID是否指向同一线程
int pthread_equal(pthread_t thread1,pthread_t thread2);
//保证init_routine线程函数在进程中只执行一次
pthread_once_t once_control = PTHREAD_ONCE_INIT;
int pthread_once(pthread_once_t *once_control, void(*init_routine)(void));

        下面为线程的创建过程,在主线程中循环创建三个子线程,这些子线程和主线程将同时执行,谁先结束取决于CPU的调度,这里通过一个sleep函数模拟子线程的退出顺序与其创建顺序相反,子线程的创建顺序可以通过pthread_create函数的第四个参数arg传递给线程函数;在某些情况下,函数执行次数要被限制为一次,我们在相称函数中通过pthread_once函数限制这些子线程只有一次机会执行run_once函数。这里需要注意的是:这些子线程和主线程共享的是同一进程地址空间,main函数作为进程的主线程,如果它提前结束意味着进程的地址空间被销毁,那么子线程的后续输出就不会再打印了,因此我们要通过一个sleep函数延迟主线程的退出。

        演示子线程的创建过程:

#include <stdio.h>
#include <pthread.h>
pthread_once_t once = PTHREAD_ONCE_INIT; //提供给pthread_once 函数
/* 定义一个要求仅运行一次的函数 */
void run_once(void)
{
        printf("  \t->thread %lu run run_once process\n ", pthread_self());
        //通过pthread_self函数显示这个函数在哪个子线程中被运行
}
//定义线程函数,也可以为三个子线程分别定义三个不同的线程函数,让其执行不同的代码
int* thread_body(void *arg)
{
        pthread_t thid;
        int seq = *(int *)arg;  // 将传递过来的参数arg转换成int型
        thid = pthread_self();
        printf(" This is %dth new thread,threadid=%lu\n ",seq,thid);
        sleep(5-seq); // 模拟子线程延迟退出
        pthread_once(&once,run_once); // 调用run_once函数,仅能被执行一次
        // run_once();
        printf(" \t->%dth thread end\n ",seq);
        return (int *)1;
}
//主线程执行main函数
int main(int argc, char *argv[])
{
        pthread_t newthid;
        int repeat = 1;
        printf(" Main thread,Thread ID is %lu\n ",pthread_self());
        while (repeat < 4)
        {
                //调用pthread_create函数创建新线程,注意实参传递的方式
                if(pthread_create(&newthid,NULL,(void *)thread_body,
                        (void *)&repeat)!=0)
                        perror(" ---thread create failed!---\n ");
                        repeat++;
        }
        sleep(5);  //延迟主线程最后退出
        printf(" Main thread end\n ");
        return 0;
        
        return 0;
}

使用以下命令进行编译:

gcc ch11-2.c -lpthread

运行结果如下图所示:

        将线程函数中的“pthread_once(&once,run_once);”注释掉,直接调用run_once()函数,结果如下:

将main函数中的sleep函数注释掉,主线程先于其他线程退出时程序的结果如下:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值