系统编程(线程操作)

错误码与处理

错误码和错误信息字符串的对应关系

C 标准库 - <errno.h>

简介

C 标准库的 errno.h 头文件定义了整数变量 errno,它是通过系统调用设置的,在错误事件中的某些库函数表明了什么发生了错误。该宏扩展为类型为 int 的可更改的左值,因此它可以被一个程序读取和修改。

<errno.h> 是 C 标准库中的一个头文件,提供了一种在程序中报告和处理错误的机制。这个头文件定义了宏和变量,用于指示和描述运行时错误的类型。

errno.h 头文件定义了一系列表示不同错误代码的宏,这些宏应扩展为类型为 int 的整数常量表达式。

errno 是一个全局变量,用于存储最近发生的错误代码。这个变量的类型为 int。当一个库函数发生错误时,它通常会设置 errno 以指示错误类型。

例如,在文件操作中,如果 fopen 函数因为文件不存在而失败,它会将 errno 设置为 ENOENT。

线程相关函数运行正常返回 0,运行错误返回错误码,没有设置全局变量 errno, 需要我们自己

封装函数处理错误码

示例代码

c 复制代码
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "header.h"

void my_perror(char* msg, int errnum)
{
    char* err_str =  strerror(errnum);
    //格式化拼接
    char errMsg[50] = {0};
    snprintf(errMsg, 50, "%s: %s\n", msg, err_str );
    puts(errMsg);
}

void my_perror2(char* msg)
{
    char* err_str =  strerror(errno);
    //格式化拼接
    char errMsg[50] = {0};
    snprintf(errMsg, 50, "%s: %s\n", msg, err_str );
    puts(errMsg);
}

int main(int argc, char const *argv[])
{
    int r = open("aaa/bbb", O_RDONLY);

    printf("num=%d\n", errno);
    my_perror("open err", errno);

    my_perror2("open error");

    if (r  == -1)
    {
        perror("open failed");
        return -1;
    }


// open failed: No such file or directory     
/*
No such file or directory       2
*/  

    // char* str = strerror(errno);
    // printf("str: %s\n", str);
    

    return 0;
}

线程

概念

线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源(栈),但它可与同属一个进程的其它线程共享进程所拥有的全部资源。同一进程中的多个线程之间可以并发执行。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。在单个程序中同时运行多个线程完成不同的工作,称为多线程程序设计。

使用理由

1.和进程相比,它是一种非常"节俭"的多任务操作方式。我们知道,在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种"昂贵"的多任务工作方式。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间,据统计一个进程的开销大概是一个线程开销的30倍左右。

2.线程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。当然,数据的共享也带来其他一些问题,比如线程间同步和互斥。

线程的基本编程操作

1.创建

线程创建实际上就是在确定线程函数调用的入口地址。

具体实现: pthread_create 函数

  1. 退出

线程创建完成后开始执行,执行完成后就退出了,但还有一种退出线程的方法

具体实现: pthread_exit 函数

  1. 取消

在很多线程应用中,经常会遇到在一个线程中要终止另一个线程的问题,此时就需要调用 pthread_cancel 函数来取消另一个线程,但在被取消的线程内部需要调用 pthread_setcancelstate 函数和 pthread_setcanceltype

4.等待

由于在一个进程中,线程是共享数据段的,退出线程所占用的资源并不会随着线程终止而得到释放,正如进程用wait来等待终止并释放资源一样,线程也有类似机制,

具体实现 pthread_join函数

5.非正常退出时,线程清理工作:

一般来说,线程终止有两种情况:正常终止和非正常终止。线程主动调用pthread_exit()或者从线程函数中return都将使线程正常退出,这是可预见的退出方式;非正常终止是线程在其他线程的干预下,或者由于自身运行出错(比如访问非法地址)而退出,这种退出方式是不可预见的。

不论是可预见的线程终止还是异常终止,都会存在资源释放的问题,在不考虑因运行出错而退出的前提下,如何保证线程终止时能顺利的释放掉自己所占用的资源,特别是锁资源,就是一个必须考虑解决的问题。

最经常出现的情形是资源独占锁的使用:线程为了访问临界资源而为其加上锁,但在访问过程中被外界取消,如果线程处于响应取消状态,且采用异步方式响应,则该临界资源将永远处于锁定状态得不到释放。外界取消操作是不可预见的,因此的确需要一个机制来简化用于资源释放的编程。

pthread_cleanup_push()/pthread_cleanup_pop()函数对用于自动释放资源

线程相关函数基本使用

pthread_self()在那个线程中,即获取那个线程的id并返回

pthread_create的形式参数:

arg1: 输出型参数,返回当前创建的线程id arg2: 创建该线程要设置或者获取线程的属性,

如果不关注属性,可以给NULL arg3: 线程要执行的任务函数 arg4: 任务函数执行时的实

际参数----通过该参数可以实现线程间的数据传递(主线程数据传递给子线程) 返回值需要

使用pthread_join的第二个参数获取---实现线程间的数据传递(子线程的数据传递给主线程)

代码示例
c 复制代码
#include <stdio.h>
#include "header.h"
/*
pthread_self()在那个线程中,即获取那个线程的id并返回
pthread_create的形式参数:
    arg1: 输出型参数,返回当前创建的线程id
    arg2: 创建该线程要设置或者获取线程的属性,如果不关注属性,可以给NULL
    arg3: 线程要执行的任务函数
    arg4: 任务函数执行时的实际参数----通过该参数可以实现线程间的数据传递(主线程数据传递给子线程)
    返回值需要使用pthread_join的第二个参数获取---实现线程间的数据传递(子线程的数据传递给主线程)
*/
void my_perror(char *msg, int errnum)
{
    if (errnum <= 0)
    {
        return;
    }
    char *err_str = strerror(errnum);
    // 格式化拼接
    char errMsg[50] = {0};
    snprintf(errMsg, 50, "%s: %s\n", msg, err_str);
    puts(errMsg);
}

// 定义子线程执行的任务函数
void *routine(void *arg)
{
    int *p = (int *)arg;
    printf("%lu--routine: a=%d\n", pthread_self(), *p);
    /*
    注意:不能返回局部变量的地址
        1.可以返回堆内存空间的地址
        2.返回静态局部变量的地址----静态变量生命周期太长,建议少用
    */
    double b = 123.321;
    // 申请堆内存
    double *retval = (double *)calloc(1, sizeof(double));
    // 转存数据
    *retval = b;
    return retval;
}

int main(int argc, char const *argv[])
{
    // 打印主线程id
    printf("main1: %lu\n", pthread_self());

    int a = 10;
    // 定义线程id的变量
    pthread_t t1;
    // 调用函数创建线程(子线程)
    int r = pthread_create(&t1, NULL, routine, &a);
    printf("t1=%lu\n", t1);

    if (r != 0)
    {
        my_perror("pthread create failed", r);
    }

    // 等待子线程的退出
    // int r2 = pthread_join(t1, NULL);
    double* pvalue = NULL; 
    int r2 = pthread_join(t1, (void**)&pvalue);
    //打印子线程返回给主线程的数据
    printf("value=%lf\n", *pvalue);
    my_perror("pthread_join failed", r2);
    
    //释放堆内存空间
    free(pvalue);

    return 0;
}

指针函数不能返回局部变量的地址

  1. 可以返回堆内存空间的地址
  2. 返回静态局部变量的地址----静态变量生命周期太长,建议少用

任务函数中提前结束线程

//调用pthread_exit()函数结束当前线程,并携带数据给其他线程 //pthread_join等待子线程执

行结束, 并用第二个参数获取线程结束携带的数据

示例代码
c 复制代码
#include <stdio.h>
#include "header.h"

#define PI 3.1415

void my_perror(char *msg, int errnum)
{
    if (errnum <= 0)
    {
        return;
    }
    char *err_str = strerror(errnum);
    // 格式化拼接
    char errMsg[50] = {0};
    snprintf(errMsg, 50, "%s: %s\n", msg, err_str);
    puts(errMsg);
}

/*
子线程中打印半径为1~100之间的整数,并计算圆形的面积,当面积大于100时停止
 */

void* task(void* arg)
{
    int num = *(int*)arg;
    //打印当前线程id和传递进来的数据
    printf("task: id=%lu, *arg=%d\n", pthread_self(), num);
    //定义变量存储面积
    double area = 0;
    //遍历1~100之间的数据
    int i;
    for(i = 1; i<=num; i++)
    {
        //计算圆形面积
        area = PI * i * i;
        if (area > 100)
        {
            //break;
            
            //通过任务函数的返回值,返回大于100的面积
            double * p = (double*)malloc(sizeof(double));
            *p = area;
            //调用pthread_exit()函数结束当前线程,并携带数据给其他线程
            pthread_exit(p);
        }
        
        printf("i=%d\n", i);
    }
    // pthread_exit函数调用导致线程提前结束,下面的代码不会被执行
    printf("i2=%d\n", i);

    /*
    //通过任务函数的返回值,返回大于100的面积
    double * p = (double*)malloc(sizeof(double));
    *p = area;
    return p;
    */
   return NULL;
}


int main(int argc, char const *argv[])
{
    printf("main-start: id=%lu\n", pthread_self());

    //定义数据并传递给子线程
    int a = 100;
    //定义线程id的变量
    pthread_t t1;
    int r = pthread_create(&t1, NULL, task, &a);
    if (r != 0)
    {
        my_perror("pthread_create  failed", r);
        return -1;
    }
   
    //定义变量接收子线程的返回值
    double* retval =  NULL;

    //等待子线程执行结束
    pthread_join(t1, (void**)&retval);
    //打印子线程返回的数据
    printf("main--*retval: %lf\n", *retval);

    //回收堆内存
    free(retval);

    printf("main-end: id=%lu\n", pthread_self());
    return 0;
}

多线程执行的特点

创建多个线程,可以执行同一个任务函数,也可以执行不同的任务函数 多线程在执行时,谁

先执行谁后执行不确定。主要看谁抢占到了CPU的执行权

取消点有哪些呢?

1:通过pthread_testcancel 调用已编程方式建立线程取消点

2:线程等待pthread_cond_wait或pthread_cond_timewait中的

特定条件

3:被sigwait阻塞的函数

4:一些标准的库调用。通常这些调用包括线程可基于阻塞的函数

根据POSIX标准

pthread_join、pthread_testcancel,pthread_cond_wait、

pthread_cond_timedwait、sem_wait、sigwait等函数以及

read()、write()等会引起阻塞的系统调用都是取消点.

c 复制代码
#include <stdio.h>
#include "header.h"

#define PI 3.1415

/*
多线程在执行时,谁先执行谁后执行不确定。主要看谁抢占到了CPU的执行权
*/

void my_perror(char *msg, int errnum)
{
    if (errnum <= 0)
    {
        return;
    }
    char *err_str = strerror(errnum);
    // 格式化拼接
    char errMsg[50] = {0};
    snprintf(errMsg, 50, "%s: %s\n", msg, err_str);
    puts(errMsg);
}

/*
子线程中打印半径为1~100之间的整数,并计算圆形的面积,当面积大于100时停止
 */

void *task(void *arg)
{
    int num = *(int *)arg;
    // 打印当前线程id和传递进来的数据
    printf("task: id=%lu, *arg=%d\n", pthread_self(), num);
    // 定义变量存储面积
    double area = 0;
    // 遍历1~100之间的数据
    int i;
    for (i = 1; i <= num; i++)
    {
        // 计算圆形面积
        area = PI * i * i;
        if (area > 100)
        {
            // break;

            // 通过任务函数的返回值,返回大于100的面积
            double *p = (double *)malloc(sizeof(double));
            *p = area;
            // 调用pthread_exit()函数结束当前线程,并携带数据给其他线程
            pthread_exit(p);
        }

        printf("id=%lu, i=%d\n", pthread_self(), i);
    }
    // pthread_exit函数调用导致线程提前结束,下面的代码不会被执行
    printf("i2=%d\n", i);

    /*
    //通过任务函数的返回值,返回大于100的面积
    double * p = (double*)malloc(sizeof(double));
    *p = area;
    return p;
    */
    return NULL;
}

//多个线程执行不同的函数
void* fun2(void* arg)
{
    printf("fun2.....\n");
    return NULL;
}


//两个子线程执行同一个任务函数

int main(int argc, char const *argv[])
{
    printf("main-start: id=%lu\n", pthread_self());

    // 定义数据并传递给子线程
    int a = 100;
    // 定义线程id的变量
    pthread_t t1;
    int r = pthread_create(&t1, NULL, task, &a);
    if (r != 0)
    {
        my_perror("pthread_create  failed", r);
        return -1;
    }

    // 定义线程id的变量
    pthread_t t2;
    int r2 = pthread_create(&t2, NULL, task, &a);
    if (r2 != 0)
    {
        my_perror("pthread_create  failed", r);
        return -1;
    }

    printf("t1=%lu, t2=%lu\n", t1, t2);

    // 定义变量接收子线程的返回值
    double *retval = NULL;

    // 等待子线程执行结束
    pthread_join(t1, (void **)&retval);
    // 打印子线程返回的数据
    printf("main--t1--*retval: %lf\n", *retval);

    // 等待子线程执行结束
    pthread_join(t2, (void **)&retval);
    // 打印子线程返回的数据
    printf("main--t2--*retval: %lf\n", *retval);

    // 回收堆内存
    free(retval);

    printf("main-end: id=%lu\n", pthread_self());
    return 0;
}

这段代码是一个多线程程序。主要的逻辑是创建两个子线程,每个子线程执行一个任务函数task。任务函数的功能是打印半径为1~100之间的整数,并计算圆形的面积,当面积大于100时停止。子线程执行完任务后,通过pthread_exit函数结束线程,并返回面积值。

在主函数中,首先创建了两个子线程t1和t2并分别调用任务函数task。然后通过pthread_join函数等待子线程执行结束,并接收子线程的返回值。最后,打印子线程返回的面积值,并释放堆内存。

需要注意的是,多线程在执行时,谁先执行谁后执行是不确定的,主要取决于谁抢占到了CPU的执行权。因此,在打印子线程返回的面积值时,可能是先打印t1的面积值,再打印t2的面积值,也可能反过来。

另外,在任务函数task中,如果面积大于100,使用动态内存分配返回面积值,并通过pthread_exit函数结束线程。如果不大于100,任务函数会继续执行直到循环结束。

线程的取消特点

多线程执行时,默认情况下:pthread_cancel它的执行把握不住:不能确定它是否有效 可

以在任务函数中调用 pthread_setcancelstate函数设置取消状态: pthread_setcancelstate

(PTHREAD_CANCEL_DISABLE, NULL); --- 设置任务的取消状态:不可取消--

PTHREAD_CANCEL_DISABLE pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, NULL); --- 设置任务的取消状态:可取消---PTHREAD_CANCEL_ENABLE (默认值)

c 复制代码
#include <stdio.h>
#include "header.h"

#define PI 3.1415

/*
多线程在执行时,谁先执行谁后执行不确定。主要看谁抢占到了CPU的执行权
*/

void my_perror(char *msg, int errnum)
{
    if (errnum <= 0)
    {
        return;
    }
    char *err_str = strerror(errnum);
    // 格式化拼接
    char errMsg[50] = {0};
    snprintf(errMsg, 50, "%s: %s\n", msg, err_str);
    puts(errMsg);
}

/*
子线程中打印半径为1~100之间的整数,并计算圆形的面积,当面积大于100时停止
 */

void *task(void *arg)
{
  //设置任务的取消状态:不可取消---PTHREAD_CANCEL_DISABLE
   //pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
   //设置任务的取消状态:可取消---PTHREAD_CANCEL_ENABLE(默认值)
   pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);

    int num = *(int *)arg;
    // 打印当前线程id和传递进来的数据
    printf("task: id=%lu, *arg=%d\n", pthread_self(), num);
    // 定义变量存储面积
    double area = 0;
    // 遍历1~100之间的数据
    int i;
    for (i = 1; i <= num; i++)
    {
        // 计算圆形面积
        area = PI * i * i;
        if (area > 100)
        {
            // break;

            // 通过任务函数的返回值,返回大于100的面积
            double *p = (double *)malloc(sizeof(double));
            *p = area;
            // 调用pthread_exit()函数结束当前线程,并携带数据给其他线程
            pthread_exit(p);
        }

        printf("id=%lu, i=%d\n", pthread_self(), i);
    }
    // pthread_exit函数调用导致线程提前结束,下面的代码不会被执行
    printf("i2=%d\n", i);

    /*
    //通过任务函数的返回值,返回大于100的面积
    double * p = (double*)malloc(sizeof(double));
    *p = area;
    return p;
    */
    return NULL;
}

//多个线程执行不同的函数
void* fun2(void* arg)
{
    printf("fun2.....\n");
    return NULL;
}


//两个子线程执行同一个任务函数

int main(int argc, char const *argv[])
{

    printf("main-start: id=%lu\n", pthread_self());

    // 定义数据并传递给子线程
    int a = 100;
    // 定义线程id的变量
    pthread_t t1;
    int r = pthread_create(&t1, NULL, task, &a);
    if (r != 0)
    {
        my_perror("pthread_create  failed", r);
        return -1;
    }

    // 定义线程id的变量
    pthread_t t2;
    int r2 = pthread_create(&t2, NULL, task, &a);
    if (r2 != 0)
    {
        my_perror("pthread_create  failed", r);
        return -1;
    }

    printf("t1=%lu, t2=%lu\n", t1, t2);


     //调用pthread_cancel函数取消线程
    //多线程执行时,默认情况下:pthread_cancel它的执行把握不住:不能确定它是否有效
    pthread_cancel(t1);
    pthread_cancel(t2);

    // 定义变量接收子线程的返回值
    double *retval = NULL;

    // 等待子线程执行结束
    pthread_join(t1, (void **)&retval);
    // 打印子线程返回的数据
    //printf("main--t1--*retval: %lf\n", *retval);

    // 等待子线程执行结束
    pthread_join(t2, (void **)&retval);
    // 打印子线程返回的数据
    //printf("main--t2--*retval: %lf\n", *retval);

    // 回收堆内存
    //free(retval);

    printf("main-end: id=%lu\n", pthread_self());
    return 0;
}

这段代码主要是通过多线程计算圆形的面积,当面积大于100时停止计算。代码中定义了一个任务函数task,该函数通过传入的参数计算圆形的面积,当面积大于100时,使用pthread_exit函数结束当前线程,并通过返回值返回面积值给主线程。主函数中创建了两个子线程,分别执行任务函数task,并通过pthread_cancel函数取消子线程的执行。之后使用pthread_join函数等待子线程执行结束,并获取子线程的返回值打印出来。

需要注意的是,在子线程中调用pthread_exit函数结束线程时,需要动态分配内存来存储返回值,否则主线程无法获取到正确的返回值。此外,在主函数中还定义了一个自定义错误处理函数my_perror,用于打印错误信息。

在多线程执行时,并不能确定哪个线程会先执行,因为线程的执行是由CPU抢占执行权决定的。

线程属性获取和设置

可以在 pthread_create 函数之前设置线程的属性,也可以在该函数之后获取线程的属性 设置

属性的函数:pthread_attr_setxxx , 获取属性值的函数:pthread_attr_getxxx, xxx 表示属性的名

称,具体看下图和 txt 文档:

c 复制代码
#include <stdio.h>
#include "header.h"

#define PI 3.1415

/*
多线程在执行时,谁先执行谁后执行不确定。主要看谁抢占到了CPU的执行权
*/

void my_perror(char *msg, int errnum)
{
    if (errnum <= 0)
    {
        return;
    }
    char *err_str = strerror(errnum);
    // 格式化拼接
    char errMsg[50] = {0};
    snprintf(errMsg, 50, "%s: %s\n", msg, err_str);
    puts(errMsg);
}

/*
子线程中打印半径为1~100之间的整数,并计算圆形的面积,当面积大于100时停止
 */
/*
在创建线程之前可以设置线程的属性
    detachstate:
        0   :  非分离状态:主线程需要调用join函数等待子线程的执行结束;否则当主线程执行结束时,会先终止子线程,然后退出;(主线程会处理子线程回收子线程的资源)
        1   :  分离状态: 主线程和子线程没有关联关系;子线程自己会执行完毕,子线程自己回收资源;
    注意:
        当主线程执行完毕后,主进程会退出,进程的虚拟内存空间会被销毁,整个程序就结束了
        所有的线程共享进程的虚拟内存空间,当进程虚拟内存空间被销毁,所有线程数据也无法保留,线程也就结束了
        导致:
            子线程没有执行完就停止了
            子线程执行了,但看不到执行的效果,数据仍然是残缺的----线程创建不用分离属性
        当一个进程执行时,系统默认会打开三个文件描述符:0,1,2
*/

void *task(void *arg)
{
    for(int i = 0; i<20; i++)
    {
        printf("task: i=%d\n", i);
    }
    return NULL;
}

// 多个线程执行不同的函数
void *task2(void *arg)
{
    for(int i = 0; i<20; i++)
    {
        printf("task2: i=%d\n", i);
    }
    return NULL;
}

void *task3(void *arg)
{
    for(int i = 0; i<20; i++)
    {
        printf("task3: i=%d\n", i);
    }
    return NULL;
}


//设置线程的属性
void set_attr2(pthread_attr_t* attr)
{
    struct sched_param  param = {0};
    param.sched_priority = 10;
    pthread_attr_setschedparam(attr, &param);
    pthread_attr_setschedpolicy(attr, SCHED_FIFO);
}

void set_attr3(pthread_attr_t* attr)
{
    struct sched_param  param = {0};
    param.sched_priority = 99;
    pthread_attr_setschedparam(attr, &param);
    pthread_attr_setschedpolicy(attr, SCHED_FIFO);
}



// 两个子线程执行同一个任务函数

int main(int argc, char const *argv[])
{

    printf("main-start: id=%lu\n", pthread_self());
    pthread_attr_t attr = {0};

    //设置分离属性
     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); //非分离属性
    //pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);//分离属性

    //设置调度策略
    // pthread_attr_setschedpolicy(&attr, SCHED_RR);
    pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
    // pthread_attr_setschedpolicy(&attr, SCHED_OTHER);

    //设置调用参数---优先级
    struct sched_param  param = {0};
    param.sched_priority = 50;
    pthread_attr_setschedparam(&attr, &param);

    //设置线程的作用域:system. precess
    // pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
    pthread_attr_setscope(&attr, PTHREAD_SCOPE_PROCESS);

    pthread_t t1, t2, t3;
    int r =  pthread_create(&t1, &attr, task, NULL);

    pthread_attr_t attr2 = {0};
    set_attr2(&attr2);
    

    pthread_create(&t2, &attr2, task2, NULL);
    pthread_attr_t attr3 = {0};
    set_attr3(&attr3);
    pthread_create(&t3, &attr3, task3, NULL);

    //获取分离属性
    int state = -1;
    pthread_attr_getdetachstate(&attr, &state);
    printf("state: %d\n", state);

    //获取线程的调用策略:   多个线程执行的先后顺序
    int policy = -2;
    pthread_attr_getschedpolicy(&attr, &policy);
    printf("policy: %d\n", policy);

    //获取调度参数:sched_priority线程执行的优先级:CPU执行的可能性
    //线程的优先级更高,CPU执行这个线程的可能性更大,但不一定就先执行完某个优先级高的才执行某个优先级低的
    //struct sched_param  param = {0};
    pthread_attr_getschedparam(&attr, &param);
    printf("sched_priority: %d\n", param.sched_priority);
    int max =  sched_get_priority_max(policy);
    int min = sched_get_priority_min(policy);
    printf("min=%d, max=%d\n", min,  max);

    int scope = -3;
    pthread_attr_getscope(&attr, &scope);
    printf("scope=%d\n",scope);


    if (r!=0)
    {
        my_perror("pthread_create failed", r);
    }
    
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    pthread_join(t3, NULL);
    printf("main-end: id=%lu\n", pthread_self());
    return 0;
}

这段代码主要是多线程的使用示例代码。首先定义了一个函数my_perror用于打印错误信息。然后定义了三个任务函数task、task2、task3,分别在子线程中执行。接下去是main函数,其中创建了三个线程t1、t2、t3,分别执行任务函数task、task2、task3。在创建线程之前,对线程的属性进行了设置,包括分离属性、调度策略、调度参数和作用域。然后使用pthread_create函数创建线程,并使用pthread_join函数等待线程的执行结束。最后打印出主线程的ID。

相关推荐
清风-云烟15 分钟前
使用redis-cli命令实现redis crud操作
java·linux·数据库·redis·spring·缓存·1024程序员节
Rm24 分钟前
Linux 防火墙 Systemctl 常用命令速查
linux
孤寂大仙v36 分钟前
【Linux】环境变量
linux·运维·服务器
RayTz1 小时前
STM32-CAN总线
网络·stm32·嵌入式硬件
贾贾20231 小时前
配电自动化中的进线监控技术
大数据·运维·网络·自动化·能源·制造·信息与通信
DADIAN_GONG1 小时前
how to use | in Linux? give me an example
linux·运维·服务器
我想学LINUX2 小时前
【2024年华为OD机试】(C/D卷,200分)- 5G网络建设 (JavaScript&Java & Python&C/C++)
java·c语言·javascript·网络·python·5g·华为od
新知图书2 小时前
Linux C\C++编程-文件位置指针与读写文件数据块
linux·c语言·c++
geffen082 小时前
HDBaseT和KVM 和POE是怎么融合在一块的
linux
不一样的信息安全2 小时前
Spring Boot框架下的上海特产销售商城网站开发之旅
网络·spring boot