Linux学习之多线程编程(线程的创建和生存周期)

言之者无罪,闻之者足以戒。 ——《诗序》

Linux多线程编程:

线程:一个正在执行的程序,它是资源分配的最小单位;进程中的事情需要按照一定的顺序逐个进行。

那么什么是线程?
线程:有时又称轻量级进程,程序执行的最小单位,系统独立调度和分析cpu的基本单位,它是进程中的一个实体。一个进程中可以有多个线程,这些线程共享进程的所有资源,线程本身只包含一点必不可少的资源。

线程:

(1)并发:是指在同一时刻,只能有一条指令执行,但多个进程指令被快速轮换执行,使得在宏观上具有多个进程同时执行的效果。(看起来是同时发生的)(单核)

(2)并行:是指在同一时刻,有多条指令在多个处理器上同时执行。(真正的同时发生)

(3)同步:彼此有依赖关系的调用不应该“同时发生”,而同步就是要阻止那些“同时发生”的事情。

(4)异步:异步是和同步的概念是相对的,任何两个彼此独立的操作是异步的,它表明事情独立的发生

多线程的优势:

(1)在多处理器中开发程序的并行性

(2)在等在慢速IO操作时,程序可以执行其他操作,提高并发性

(3)模块化的编程,能更清晰的表达程序中独立事件的关系,结构清晰

(4)占用较少的系统资源

(多线程不一定要多处理器)

一、线程的创建和生命

1、创建新的线程:

  线程 进程
标识符类型 pthread_t pid_t
获取id pthread_self() getpid()
创建 pthread_create() frok()

(1) pthread_self

pthread_self(void)

头文件: #include <pthread.h>

参数:无

返回值:线程的ID

注意:在编译的时候要加上 -lpthread

下面通过一个小程序学习一下这个函数:

#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 %X\n",pid,tid);

        return 0;
}

(2)pthread_create

 int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

头文件:#include <pthread.h>

第一个参数:新线程的ID,如果成功则新线程的ID会填充到thread指向的内存

第二个参数:线程属性(调度策略、继承性、分离性等)(可以写NULL)

第三个参数:回调函数(新线程要执行的函数)

第四个参数:回调函数的参数

返回值:成功返回0,失败则返回错误码

注意:编译时要连接库libpthread;就是编译的时候要加 -lpthread

直接给出程序:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
void print_id(char *s)
{
        pid_t pid;
        pthread_t tid;

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

        printf("%s pid is %u,tid is %X\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 thread");
        if(err != 0)
        {
                printf("create new thread failure\n");
                return -1;
        }
        print_id("main thread:");
        sleep(2);
//      print_id("main thread:");
        return 0;
}

2、线程的生命周期

(1)当c程序运行时,首先运行main函数。在线程代码中,这个特殊的执行流被称作初始线程或者主线程。你可以在初始化线程中做任何普通线程可以做的事。

(2)主线程的特殊性在于,它在main函数返回的时候会导致进程结束,进程内所有的线程也将会结束。这可不是一个好事情,你可以在主线程中调用pthread_exit函数,这样进程就会等待所有的线程结束才终止。

(3)主线程接收参数的方式是通过argc和argv,而普通的线程只有一个参数void *

(4)在绝大多数情况下,主线程在默认堆栈上运行,这个堆栈可以增长到足够的长度,而普通线程的堆栈是受限制的,一旦溢出就会产生错误

(5)主线程是跟随着进程的创建而创建

(6)其他线程可以通过调用函数来创建,主要调用pthread_create

(7)新线程可能在当前线程从函数pthread_create返回之前就已经运行了,甚至新线程可能早当前线程从函数pthread_create返回之前就已经运行结束了。

(1)线程的创建:

下面写一个程序:


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

struct student
{
        int age;
        char name[20];
        char id[4];
};

void *thread_fun(void *stu)
{
        sleep(1);
        printf("student age is %d, name is %s, id is %s\n",((struct student *)stu)->age,((struct student *)stu)->name,((struct student *)stu)->id);
        return (void *)0;
}
int main(int argc, char *argv[])
{
        pthread_t tid;
        int err;
        int *rval;
        int i;
        struct student stu;
        stu.age = 20;
        memcpy(stu.name,"xiaoyi",20);
        memcpy(stu.id,"007",4);
        err=pthread_create(&tid,NULL,thread_fun,(void *)(&stu));
        if(err !=0)
        {
                printf("create new thread failure\n");
                return -1;
        }
        printf("main thread have %d\n",argc);
        for(i=0;i<argc;i++)
        {
                printf("main thread args is %s\n",argv[i]);
        }
//      sleep(1);
        pthread_exit(rval);
}

这个程序我们基本上把上面所有的函数都用上了。

(2)线程的四个基本状态:

状态 含义
就绪 线程能够运行,但是在等待可用的处理器
运行 线程正在运行,在多核系统中,可能同时有多个线程在运行
阻塞 线程在等待处理器以外的其他条件
终止 线程从启动函数中返回,或者调用pthread_exit函数,或者被取消

1)、当线程刚被创建时就处于就绪状态,或者当线程被解除阻塞以后会处于就绪状态。就绪的线程在等待一个可以用的处理器,当一个运行的线程被抢占时,它立刻又回到就绪状态。

2)、当处理器选中一个就绪的线程执行时,它立刻变成运行状态。

3)、线程会在一下情况下发生阻塞:试图加锁一个已经被锁住的互斥量,等待某个条件变量,调用singwait等待尚未发生的信号,执行无法完成的IO信号,由于内存页错误。

4)、线程通常启动函数中返回来终止自己,或者调用pthread_exit退出,或者取消线程

(3)回收:

线程的分离属性:分离一个正在运行的线程并不影响它,仅仅是通知当前系统该线程结束时,其所属的资源可以回收,一个没有被分离的线程在终止时会保留它的虚拟内存,包括他们的堆栈和其他系统资源,有时这种线程被称为“僵尸线程”。创建线程时默认是非分离的。

如果线程具有分离属性,线程终止时会被立刻回收,回收将释放掉所有在线程终止时未释放的系统资源和进程资源,包括保存线程返回值的内存空间,堆栈,保存寄存器的内存空间等。

终止被分离的线程会释放所有的系统资源,但是你必须释放由该线程占有的程序资源。由malloc或mmap分配的内存可以在任何时候由任何线程释放,条件变量、互斥量、信号灯可以由任何线程销毁,只要他们被解锁了或没有线程等待。但是只有互斥量的主人才能解锁它,所以在线程终止前,你需要解锁互斥量。

猜你喜欢

转载自blog.csdn.net/weixin_42994525/article/details/83116735