线程的基本概念,线程的同步互斥机制

一、线程的概念

1 .1 什么是线程

        线程:线程是进程的一个实体,是被系统独立调度和分派的基本单位,是一个进程并发执行多个任务的机制。

        并发:单核CPU多任务同时运行,CPU以ms级进行进程调度

1.2 为什么引入线程

        进程间的切换表现为上下文的切换:

        上下文:运行一个进程所需要的所有资源

        上下文切换:从访问进程1到访问进程2,CPU访问的资源要替换原有内容,这个操作本身是一个耗时操作

        为了减少进程创建、撤销和切换时所付出的时空开销,使操作系统有更好的并发性,把资源的分配单位和调度单位分开,提出了线程。

        线程属于进程,每一个进程至少有一个线程作为指令执行体,线程运行在进程空间内,一个进程中可以有多个线程,因此程序也被称为多线程程序。

1.3 进程与线程的区别 

  1. 进程是资源分配的最小单位,线程是任务运行的最小单位!!!
  2. 进程与进程的用户空间相互独立,内核空间是所有进程共享,进程与进程之间的通信,需要引入进程间通信机制才能实现
  3. 线程与线程共享其附属进程的所有资源,线程运行在进程空间内,线程与线程之间的通信,需要注意同步互斥
  4. 创建多线程的效率比创建多进程高
  5. 多进程的资源量比多线程高
  6. 多进程稳定性比多线程高

二、线程的相关函数

2.1 pthread_create

 功能:创建一个线程

函数原型:

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

 参数:

        pthread_t *thread:存储创建后的线程id号

        pthread_attr_t *attr:线程的属性,填NULL。如果要设置,该位置需要通过pthread_attr_init初始化属性,可以设置分离属性              void *(*start_routine) (void *):函数指针,该指针指向线程的任务执行体。该指针可以指向返回值是void*类型,参数列表是void*类型的函数,例如:void* callBack(void* arg){}

        void *arg:传递给函数指针指向的函数的参数

返回值:

        成功,返回0

        失败,返回错误编号,即非0

2.1.1 注意事项: 

  1. 从主函数(main())进来的线程称为主线程,pthread_create创建的线程称之为分支线程或子线程
  2. 一般来说,主线程会先运行,但实际运行情况需要按照时间片轮询机制
  3. 主线程退出后(main()函数退出),会导致进程结束,依附于该进程内的所有线程均会被强制退出
  4. 分支线程退出不会影响主线程

 2.1.2 线程的传参

        定义一个全局变量,int a = 10,主线程和分支线程均能访问到,访问到的资源为同一份资源(即临界资源)

        在主线程定义一个局部变量,int b = 10,分支线程不能访问,同样,在分支线程定义一个局部变量,int c = 10,主线程不能访问。因为局部变量的生命周期以及作用域有限,那么如何实现这些资源的共享?

        1)主线程传参给分支线程

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

//线程执行体
void* callBack(void* arg)   //void* arg = &c
{
    while(1)
    {
        printf("this is other func c=%d %p __%d__\n", *(int *)arg, arg,__LINE__);                    
        sleep(1);
    }

    return NULL;
}


int main(int argc, const char *argv[])
{
    //主线程定义一个局部变量
    int c = 10;
    //创建线程
    pthread_t tid;
    if(pthread_create(&tid, NULL, callBack, &c) != 0)
    {
        fprintf(stderr, "pthread_create failed __%d__\n", __LINE__);
        return -1;
    }

    while(1)
    {
        printf("this is main function c=%d %p __%d__\n", c, &c, __LINE__);
        sleep(1);
    }

    return 0;
}

        2)分支线程传承给主线程 

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

//线程执行体
void* callBack(void* arg)   //void* arg = &c
{
    //分支线程定义一个局部变量
    int b = 10;
    *(int *)arg = &b;
    while(1)
    {
        printf("this is other func b=%d %p __%d__\n", b, &b,__LINE__);                    
        sleep(1);
    }

    return NULL;
}


int main(int argc, const char *argv[])
{
    int get_b;
    //创建线程
    pthread_t tid;
    if(pthread_create(&tid, NULL, callBack, &get_b) != 0)
    {
        fprintf(stderr, "pthread_create failed __%d__\n", __LINE__);
        return -1;
    }

    while(1)
    {
        printf("this is main function b=%d %p __%d__\n", get_b, &get_b, __LINE__);
        sleep(1);
    }

    return 0;
}

此时,主线程与分支线程打印的变量,值相等,地址不相等,若想它们的地址也相等我们可以用以下方法:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

//线程执行体
void* callBack(void* arg)   //void* arg = &c
{
    //分支线程定义一个局部变量
    int b = 10;
    *(int **)arg = &b;
    while(1)
    {
        printf("this is other func b=%d %p __%d__\n", b, &b,__LINE__);                    
        sleep(1);
    }

    return NULL;
}


int main(int argc, const char *argv[])
{
    int *get_b = NULL;
    //创建线程
    pthread_t tid;
    if(pthread_create(&tid, NULL, callBack, &get_b) != 0)
    {
        fprintf(stderr, "pthread_create failed __%d__\n", __LINE__);
        return -1;
    }

    while(1)
    {
        printf("this is main function b=%d %p __%d__\n", *get_b, get_b, __LINE__);
        sleep(1);
    }

    return 0;
}

2.2 pthread_exit 

功能:退出线程,并传递线程退出状态值,线程退出后,会残留一部分资源,该资源可以被pthread_join回收

函数原型:

#include <pthread.h> 
void pthread_exit(void *retval);

参数:

        void *retval:可以传递线程退出状态值,可以是任意类型数据;。数据会被pthread_join函数接收。参数可以填NULL,代表不返回任何数据

2.3 pthread_join

功能:阻塞函数,阻塞等待指定的线程退出。 线程退出后,会残留一部分资源,该资源可以被pthread_join回收

函数原型:

#include <pthread.h> 
int pthread_join(pthread_t thread, void **retval);

参数:

        pthread_t thread:指定要等待、回收哪一个线程的资源; void **retval:如果该参数不为NULL,则线程退出状态值会被拷贝到,当前指针指向的内存空间中。 填NULL,代表不接收退出状态值

返回值:

        成功,返回0

        失败,返回错误码,即非0

2.4 pthread_detach

功能:分离线程,线程退出后,由内核自动回收资源。不需要其他线程再调用pthread_join回收。当主线程有自己任务的时候,无法用pthread_join回收线程资源时,选择pthread_detach函数,自动回收资源

函数原型:

#include <pthread.h> 
int pthread_detach(pthread_t thread);

参数:

        pthread_t thread:指定要分离的线程的tid号

返回值:

        成功,返回0

        失败,返回错误码,即非0

注意:当使用pthread_detach分离指定线程后,pthread_join函数将无法回收该线程资源,且pthread_join函数不再阻塞

2.5 pthread_cancel

功能:用一个线程请求另外一个指定线程退出,请求成功,不代表目标线程一定会退出

函数原型:

#include <pthread.h> 
int pthread_cancel(pthread_t thread);

参数:

         pthread_t thread:指定要请求哪个线程退出,填对应的tid号

返回值:

        成功,返回0

        失败,返回错误码,即非0

注意事项:

  1. pthread_cancel会给目标线程打上一个退出标签,CPU切换到目标线程后运行退出标签才能退出线程。
  2. while循环、for循环等循环结构,以及算术运算等位置,无法打上退出标识,因此使用pthread_cancel函数无法退出线程
  3. printf、sleep等需要耗时操作的函数可以打上退出标识

猜你喜欢

转载自blog.csdn.net/weixin_53478812/article/details/132090953