linux线程

进程:一个正在执行的程序,它是资源分配的最小单位

进程中事情需要按照一定的顺序逐个进行,那么如何让一个进程中的一些事情同时执行?

线程:线程(thread)是包含在进程内部的顺序执行流,是进程中的实际运作单位,也是操作 系统能够进行调度的最小单位。一个进程中可以并发多条线程,每条线程并行执行不同的任 务。

线程与进程的关系可以归结于以下几点:
1、一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个主线程;
2、资源分配给进程,同一进程的所有线程共享该进程的所有资源;
3、线程作为调度和分配的基本单位,进程作为拥有资源的基本单位;
4、进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的 资源;
5、在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销大于创 建或撤消线程时的开销

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
多进程程序结构和多线程程序结构有很大的不同,多线程程序结构相对以多进程程序结 构有以下的优势
(1)方便的通信和数据交换
(2)更高效的利用 CPU
(3)模块化的编程,能更清晰的表达程序中独立事情的关系,结构清晰

线程的一些术语
并发:并发是指在同一时刻,只能有一条指令执行,但多条进程指令被快速轮换执行,使得在宏观上具有多个进程同时执行的效果。(看起来同时发生,但是是单核)
并行:并行是指在同一时刻,有多条指令在多个处理器上同时执行(真正的同时发生)
同步:彼此有依赖关系的调用不应该“同时发生”,而同步就是要阻止那些“同时发生”的事情
异步:异步和同步的概念相对的,任何两个彼此独立的操作是异步的,它表明事情独立的发生

线程管理
线程管理包含了线程的创建、终止、等待、分离、设置属性等操作

线程 ID
Pthreads 线程有一个 pthread_t 类型的 ID 来引用。线程可以通过调用 pthread_self()函数 来获取自己的 ID。pthread_self()函数原型如下
pthread_t pthread_self(void);
该函数返回调用线程的线程 ID
在这里插入图片描述
pthread_self() 线程的ID

#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<string.h>
int main()
{
        pid_t pid;
        pthread_t tid;

        pid = getpid();
        tid = pthread_self();

        printf("pid is %u, tid is %u\n",pid,tid);
        return 0;
}

在这里插入图片描述

创建线程

  1. 在进程中创建一个新线程的函数是 pthread_create(),原型如下
    int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

参数说明:
1、 thread 用指向新创建的线程的 ID;
2、attr 用来表示一个封装了线程各种属性的属性对象,如果 attr 为 NULL,新线程就 使用默认的属性,13.3.4 节将讨论线程属性的细节;
3、start_routine 是线程开始执行的时候调用的函数的名字,start_routine 函数有一个有 指向 void 的指针参数,并有 pthread_create 的第四个参数 arg 指定值,同时 start_routine 函数返回一个指向 void 的指针,这个返回值被 pthread_join 当做退出 状态处理,13.3.3 节介绍线程的退出状态;
4、arg 为参数 start_routine 指定函数的参数。

返回值说明:
1、如果 pthread_create()调用成功,函数返回 0,否则返回一个非 0 的错误码

在这里插入图片描述

/*
getpid()   获取进程ID
pthread_self()  获取线程ID

int pthread_create(新线程ID,新线程属性 默认为NULL,新线程到启动函数,传给新线程),
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<pthread.h>

void print_id(char *s)
{
        pid_t pid;
        pthread_t tid;

        pid = getpid();
        tid = pthread_self();

        printf("%s pid is %u,tid is %u\n",s,pid,tid);
}
void *thread_fun(void *arg)
{
        print_id(arg);
        return (void *)0;
}

int main()
{
        pthread_t ntid;
        int err;

        err = pthread_create(&ntid,NULL,thread_fun,"new_rhread\n");
        if(err != 0)
        {
                printf("creat new thread failure\n");
                return 0;
        }
        print_id("main thread");
        sleep(2);
        return 0;
}

在这里插入图片描述
这里创造了一个线程和进程,并打印线程和进程的ID

初始化线程/主线程
1、当C程序运行时,首先运行main函数,在线程代码中,这个特殊的执行流被称作为初始化线程或者主线程,你可以在初始化线程中做任何普通线程可以做的事情
2、主线程的特殊在于,它在main函数返回的时候,会导致进程结束,进程内所有的线程也将会结束。这可不是一个好现象,你可以在主线程中调用pthread_exit函数,这样进程就会等待所有线程结束才会终止。
3、在大多情况下,主线程在默认堆栈上运行,这个堆栈可以增长到足够的长度,而普通线程的堆栈是受限制的,一旦一处就会产生错误。
4、主线程参数的方式是通过argc和argv,而普通的线程只有一个参数void
在这里插入图片描述

在这里插入图片描述

/*
getpid()   获取进程ID
pthread_self()  获取线程ID

int pthread_create(新线程ID,新线程属性 默认为NULL,新线程到启动函数,传给新线程),
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<pthread.h>

struct student
{
        int age;
        char name[20];
};
//初始化线程的启动函数,格式要符合第三个参数void *(start_routine)(void *)
void *thread_fun(void *stu)  //新线程
{
        printf("student age is %d,name is %s",((struct student *)stu)->age,((struct student *)stu)->name);//这里输出不能直接引用结构体里面的变量,所以要强制转化
        return (void *)0;
}
int main(int argc,char *argv[])
{
        pthread_t ntid;
        int err;
        struct student stu;
        stu.age = 20;
        int i;
        memcpy(stu.name,"zhanhan\n",20);//不能直接赋予结构体,而应该用memcpy给结构体初始化

        err = pthread_create(&ntid,NULL,thread_fun,(void *)(&stu));
        if(err != 0)
        {
                printf("creat new thread failure\n");
                return 0;
        }

        printf("main thread %d\n",argc);//打印参数的个数
        for(i = 0; i < argc; i++)
        {
                printf("main thread argc is %s\n",argv[i]);//打印出给出的字符串

        }
        sleep(1);//睡眠一秒,确保线程的运行  如果不休眠,则新线程的东西不回打印出来
        
        return 0;
        //这里的return 0 可以改成pthread_exit(rval);   rval自动回填,要定义 int *ravl
}

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. 进程的终止可以通过直接调用 exit()、执行 main()中的 return、或者通过进程的某个其它 线程调用 exit()来实现。在以上任何一种情况下,所有的线程都会终止。如果主线程在创建 了其它线程后没有任务需要处理,那么它应该阻塞等待所有线程都结束为止,或者应该调用 pthread_exit(NULL)。 调用 exit()函数会使整个进程终止,而调用 pthread_exit()只会使得调用线程终止,同时 在创建的线程的顶层执行 return 线程会隐式地调用 pthread_exit()。pthread_exit()函数原型如 下:
    void pthread_exit(void *retval);

retval 是一个 void 类型的指针,可以将线程的返回值当作 pthread_exit()的参数传入,这 个值同样被 pthread_join()当作退出状态处理。如果进程的最后一个线程调用了 pthread_exit(), 进程会带着状态返回值 0 退出。
在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<pthread.h>

void *thread_fun(void *arg)//3种方式退出
{
        //如果传入参数1,采用return退出
        if(strcmp("1",(char *)arg) == 0)
        {
                printf("new thread return\n");
                return (void *)1;
        }
        //如果传入参数2,采用Pthread_exit方式退出
        if(strcmp("2",(char *)arg) == 0)
        {
                printf("new thread prhread_exit!\n");
                pthread_exit((void *)2);
        }
        //如果传入参数3,采用exit方式退出
        if(strcmp("3",(char *)arg) == 0)
        {
                printf("new thread exit\n");
                exit(3);
        }
}

int main(int argc,char *argv[])
{
        pthread_t tid;
        int err;

        err = pthread_create(&tid,NULL,thread_fun,(void *)argv[1]);
        if(err != 0)
        {
                printf("creat new thread failure\n");
                return 0;
        }

        sleep(1);//睡眠一秒,确保线程的运行
        printf("main thread\n");
        return 0;
}

在这里插入图片描述
方式1和方式2都不会导致主线程退出,而exit会导致主线程退出

2、证明任意一个线程调用exit函数都会导致进程退出

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include<sys/ipc.h>
int num;

void *thread_fun(void *arg)
{
    printf("I'm new thread: %d\n", (int *)arg);

    //如果线程到参数arg和产生到随机数相同,那么线程就采用exit方式退出
    if(num == (int *)arg)
    {
        printf("new thread %d exit!!!\n", (int *)arg);
        exit(0);
    }
    //否则线程睡眠,让其他线程执行
    sleep(2);
    
    pthread_exit((void *)0);
}

int main()
{
    int err;
    pthread_t tid;

    //产生随机种子
    srand((unsigned int)time(NULL));
    //产生一个10以内到随机数
    num = rand()%11;

    int i=10;
    //创建10个线程,给每个线程传递参数i
    while(i--)
    {
        err = pthread_create(&tid, NULL, thread_fun, (void *)i);
        if(err != 0)
        {
            printf("create new thread failed\n");
            return 0;
        }
    }
    //睡眠1s,让新线程先运行
    sleep(1);
    return 0;
}

线程的连接
在这里插入图片描述
如果一个线程是非分离线程,那么其它线程可调用 pthread_join()函数对非分离线程进行 连接。pthread_join()函数原型如下

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

函数的参数
参数 retval 为指向线程的返回值的指针提供一个位置,这个返回值是目标线程调用 pthread_exit()或者 return 所提供的值。当目标线程无需返回时可使用 NULL 值,调用线程如 果不需对目标线程的返回状态进行检查可直接将 retval 赋值为 NULL

pthread_join()函数将调用线程挂起,直到第一个参数thread指定目标线程终止运行为止。
函数的返回值:如果 pthread_join()成功调用,它将返回 0 值,如果不成功,pthread_join()返回一个非 0 的错误码

线程的分离

  1. pthread_detach()函数可以将非分离线程设置为分离线程,函数原型如下

  2. int pthread_detach(pthread_t tid);

  3. 参数 thread 是要分离的线程的 ID。 线程可以自己来设置分离,也可以由其它线程来设置分离,以下代码线程可设置自身分 离:
    pthread_detach(pthread_self());
    成功返回 0;失败返回一个非 0 的错误码,

写例子
在这里插入图片描述

/*
pthread_join(线程ID,返回值)
pthreaddetach(线程ID)
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>

void *thread_fun1(void *arg)//线程1
{
        printf("this is first thread\n");
        return (void *)1;
}

void *thread_fun2(void *arg)//线程2
{
        printf("this is second thread\n");
        //pthread_detach(pthread_self());//分离线程2
        pthread_exit((void *)2);
}
int main(int argc, char *argv[])
{
        int err1, err2;
        pthread_t tid1, tid2;
        void *rval1, *rval2;
        int ret1, ret2;

        err1 = pthread_create(&tid1,NULL, thread_fun1,NULL);
        err2 = pthread_create(&tid2,NULL, thread_fun2,NULL);

        printf("this is main thread\n");
        if(err1 || err2)
        {
                printf("create new thread failure\n");
                return 0;
        }

        ret1 = pthread_join(tid1,&rval1);//线程的连接
        ret2 = pthread_join(tid2,&rval2);
        printf("tid1 is %d\n",ret1);
        printf("tid2 is %d\n",ret2);

        printf("thread1 exit code is %d\n",(int *)rval1);//打印rvall的返回值

        printf("thread2 exit code is %d\n",(int *)rval2);

}

在这里插入图片描述

线程的取消

在这里插入图片描述
int pthread_cancel(pthread_t tid)
取消tid指定线程,成功返回0,但是取消只是发送一个请求,并不意味着等待线程终止,而且发送成功也不意味着tid一定会终止

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值