线程(Linux系统实现)

news2024/11/18 1:31:50

目录

1. 线程概述

2.主线程和子线程

3.创建线程

线程函数

创建线程示例

4.线程退出 

线程退出的原理主要包括以下两个方面:

5.线程回收

回收子线程数据

6.线程分离

7.线程取消

8.线程 ID 比较

1. 线程概述


        线程是轻量级的进程(LWP:light weight process),但在 Linux 环境下线程的本质仍是进程。在计算机上运行的程序是一组指令及指令参数的组合,指令按照既定的逻辑控制计算机运行。操作系统会以进程为单位,分配系统资源,可以这样理解,进程是资源分配的最小单位,线程是操作系统调度执行的最小单位。

线程和进程之间的区别:

进程有自己独立的地址空间,多个线程共用同一个地址空间

线程更加节省系统资源,效率不仅可以保持的,而且能够更高
在一个地址空间中多个线程独享:每个线程都有属于自己的栈区,寄存器 (内核中管理的)
在一个地址空间中多个线程共享:代码段,堆区,全局数据区,打开的文件 (文件描述符表) 都是线程共享的
根本区别:线程是程序的最小执行单位,进程是操作系统中最小的资源分配单位

每个进程对应一个虚拟地址空间,一个进程只能抢一个 CPU 时间片
一个地址空间中可以划分出多个线程,在有效的资源基础上,能够抢更多的 CPU 时间片

CPU 的调度和切换:线程的上下文切换比进程要快的多

上下文切换:进程 / 线程分时复用 CPU 时间片,在切换之前会将上一个任务的状态进行保存,下次切换回这个任务的时候,加载这个状态继续运行,任务从保存到再次加载这个过程就是一次上下文切换。

线程更加廉价,启动速度更快,退出也快,对系统资源的冲击小。

在处理多任务程序的时候使用多线程比使用多进程要更有优势,但是线程并不是越多越好,如何控制线程的个数呢?

        文件 IO 操作:文件 IO 对 CPU 是使用率不高,因此可以分时复用 CPU 时间片,线程的个数 = 2 * CPU 核心数 (效率最高)

        处理复杂的算法 (主要是 CPU 进行运算,压力大),线程的个数 = CPU 的核心数 (效率最高)

2.主线程和子线程

主线程和子线程是多线程并发编程中常见的概念,它们的区别如下:

        1. 主线程是程序的入口,是第一个被创建和执行的线程,通常负责执行初始化工作和创建其他子线程。而子线程是主线程创建的线程,可以执行各种任务。

        2. 主线程只有一个,而子线程可以有多个。主线程负责创建和管理子线程,当所有子线程结束后,主线程也会随之结束。

        3. 主线程在进程启动时被创建,而子线程则在主线程中创建。主线程和子线程的执行顺序不固定,取决于系统的调度算法。

        4. 主线程和子线程共享进程的资源,如内存空间、文件句柄等。但是,它们之间的栈空间是独立的。

        5. 主线程和子线程可以通过各种同步机制进行通信和协作,如互斥锁、信号量、条件变量等。

        总之,主线程和子线程都是并发编程中重要的概念,主线程负责创建和管理子线程,子线程负责执行具体的任务。在实际编程中,需要合理地利用它们,充分发挥多线程的优势,提高程序的效率和性能。

3.创建线程

创建线程的本质是在进程中创建一个独立的执行流,这个执行流与主线程并行执行,共同使用进程的内存空间和其他资源。线程在用户空间被创建,但是线程切换是由操作系统内核来完成的。

创建线程的过程大致如下:

        1. 调用线程库中的线程创建函数,传递线程函数和参数,得到一个新的线程ID。

        2. 操作系统内核为该线程分配一些资源,如线程栈和寄存器上下文等。

        3. 在新线程的栈上创建一个初始的上下文,包括程序计数器等寄存器的值,将线程的入口函数和参数传递给该上下文。

        4. 线程被加入到调度队列中,等待被调用执行。

        5. 线程被调度器调度执行,执行线程函数。

        6. 线程执行完毕后,释放相关资源,退出。

        线程的调度和执行由操作系统内核来完成,其中包括线程的转换、运行队列的管理、时间片的分配等。线程的创建和销毁由线程库来管理,线程库会利用操作系统提供的系统调用来实现这些功能。

        总的来说,创建线程的本质是向操作系统请求创建一个新的执行流。操作系统会为该线程分配一些资源并建立相关的上下文,程序运行时操作系统会为线程提供调度和执行资源,从而实现多线程并发执行的功能。

线程函数:

        每一个线程都有一个唯一的线程 ID,ID 类型为 pthread_t,这个 ID 是一个无符号长整形数,如果想要得到当前线程的线程 ID,可以调用如下函数:

pthread_t pthread_self(void);	// 返回当前线程的线程ID

        在一个进程中调用线程创建函数,就可得到一个子线程,和进程不同,需要给每一个创建出的线程指定一个处理函数,否则这个线程无法工作。

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
/*
thread:用于存储新线程的标识符,该标识符可用于后续线程相关的操作。
attr:指向pthread_attr_t类型的指针,用于设置新线程的属性。如果为NULL,则使用默认属性。
start_routine:指向线程函数的指针,该函数会在新线程中执行。该函数必须返回void*类型,并接受一个void*类型的参数。如果线程函数需要传递多个参数,可以将它们封装到一个结构体中,然后将指向该结构体的指针作为arg参数传递给pthread_create()函数。
arg:指向线程函数参数的指针。如果线程函数不需要参数,则可以将其设置为NULL。
pthread_create()函数返回0表示线程创建成功,否则返回错误码。在Linux中,线程是轻量级进程,它们与主线程共享进程的内存空间。可以使用信号量、互斥量等机制来保证线程之间的互斥访问共享资源。
*/

创建线程示例:

创建一个pthread_create.c文件

 编辑以下代码

// pthread_create.c 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>

// 子线程的处理代码
void* working(void* arg)
{
    printf("我是子线程, 线程ID: %ld\n", pthread_self());
    for (int i = 0; i < 9; ++i)
    {
        printf("child == i: = %d\n", i);
    }
    return NULL;
}

int main()
{
    // 1. 创建一个子线程
    pthread_t tid;
    pthread_create(&tid, NULL, working, NULL);

    printf("子线程创建成功, 线程ID: %ld\n", tid);
    // 2. 子线程不会执行下边的代码, 主线程执行
    printf("我是主线程, 线程ID: %ld\n", pthread_self());
    for (int i = 0; i < 3; ++i)
    {
        printf("i = %d\n", i);
    }

    sleep(1);

    return 0;
}

然后gcc程序

 

4.线程退出 

        在编写多线程程序的时候,如果想要让线程退出,但是不会导致虚拟地址空间的释放(针对于主线程),我们就可以调用线程库中的线程退出函数,只要调用该函数当前线程就马上退出了,并且不会影响到其他线程的正常运行,不管是在子线程或者主线程中都可以使用。

#include <pthread.h>
void pthread_exit(void *retval);
//参数:线程退出的时候携带的数据,当前子线程的主线程会得到该数据。如果不需要使用,指定为 NULL

示例:(让主线程退出,观察子线程能否继续执行完毕)

// pthread_create.c 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>

// 子线程的处理代码
void* working(void* arg)
{
    printf("我是子线程, 线程ID: %ld\n", pthread_self());
    for (int i = 0; i < 9; ++i)
    {
        printf("child == i: = %d\n", i);
    }
    return NULL;
}

int main()
{
    // 1. 创建一个子线程
    pthread_t tid;
    pthread_create(&tid, NULL, working, NULL);

    printf("子线程创建成功, 线程ID: %ld\n", tid);
    // 2. 子线程不会执行下边的代码, 主线程执行
    printf("我是主线程, 线程ID: %ld\n", pthread_self());
    for (int i = 0; i < 3; ++i)
    {
        printf("i = %d\n", i);
    }

    pthread_exit(NULL);

    return 0;
}

发现让主线程退出,不影响子线程执行 

 

线程退出的原理主要包括以下两个方面:

1. 线程主动退出:线程可以通过调用pthread_exit函数来主动退出,该函数接受一个参数,表示线程的返回值。当线程调用pthread_exit函数后,线程就会立即退出,并将其返回值传递给其它线程。

2. 线程被动退出:当一个线程执行完毕或者出现异常时,它就会被动退出。在这种情况下,操作系统会自动回收线程的资源,并向其它线程发送一个线程退出信号。

无论是线程主动退出还是被动退出,线程在退出前都需要做一些善后处理工作,例如释放其占用的资源、关闭文件、清理内存等。在多线程编程中,如果线程退出不当,会导致内存泄露、死锁等问题,因此需要特别注意线程的退出处理。通常建议在线程中使用异常处理机制来处理可能发生的异常情况,以确保线程能够正常退出并释放其资源。

5.线程回收

        线程和进程一样,子线程退出的时候其内核资源主要由主线程回收,线程库中提供的线程回收函叫做 pthread_join(),这个函数是一个阻塞函数,如果还有子线程在运行,调用该函数就会阻塞,子线程退出函数解除阻塞进行资源的回收,函数被调用一次,只能回收一个子线程,如果有多个子线程则需要循环进行回收。

另外通过线程回收函数还可以获取到子线程退出时传递出来的数据,函数原型如下:

#include <pthread.h>
// 这是一个阻塞函数, 子线程在运行这个函数就阻塞
// 子线程退出, 函数解除阻塞, 回收对应的子线程资源, 类似于回收进程使用的函数 wait()
int pthread_join(pthread_t thread, void **retval);
/*
thread: 要被回收的子线程的线程 ID

retval: 二级指针,指向一级指针的地址,是一个传出参数,这个地址中存储了 pthread_exit () 传递出的数据,如果不需要这个参数,可以指定为 NULL

返回值:线程回收成功返回 0,回收失败返回错误号。


*/

回收子线程数据

        在子线程退出的时候可以使用 pthread_exit() 的参数将数据传出,在回收这个子线程的时候可以通过 phread_join() 的第二个参数来接收子线程传递出的数据。接收数据有很多种处理方式,下面来列举几种:

使用子线程栈
        通过函数 pthread_exit(void *retval); 可以得知,子线程退出的时候,需要将数据记录到一块内存中,通过参数传出的是存储数据的内存的地址,而不是具体数据,由因为参数是 void* 类型,所有这个万能指针可以指向任意类型的内存地址。先来看第一种方式,将子线程退出数据保存在子线程自己的栈区:

// pthread_join.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>

// 定义结构
struct Persion
{
    int id;
    char name[36];
    int age;
};

// 子线程的处理代码
void* working(void* arg)
{
    printf("我是子线程, 线程ID: %ld\n", pthread_self());
    for (int i = 0; i < 9; ++i)
    {
        printf("child == i: = %d\n", i);
        if (i == 6)
        {
            struct Persion p;
            p.age = 12;
            strcpy(p.name, "tom");
            p.id = 100;
            // 该函数的参数将这个地址传递给了主线程的pthread_join()
            pthread_exit(&p);
        }
    }
    return NULL;	// 代码执行不到这个位置就退出了
}

int main()
{
    // 1. 创建一个子线程
    pthread_t tid;
    pthread_create(&tid, NULL, working, NULL);

    printf("子线程创建成功, 线程ID: %ld\n", tid);
    // 2. 子线程不会执行下边的代码, 主线程执行
    printf("我是主线程, 线程ID: %ld\n", pthread_self());
    for (int i = 0; i < 3; ++i)
    {
        printf("i = %d\n", i);
    }

    // 阻塞等待子线程退出
    void* ptr = NULL;
    // ptr是一个传出参数, 在函数内部让这个指针指向一块有效内存
    // 这个内存地址就是pthread_exit() 参数指向的内存
    pthread_join(tid, &ptr);
    // 打印信息
    struct Persion* pp = (struct Persion*)ptr;
    printf("子线程返回数据: name: %s, age: %d, id: %d\n", pp->name, pp->age, pp->id);
    printf("子线程资源被成功回收...\n");

    return 0;
}


运行结果:

 

使用全局变量
        位于同一虚拟地址空间中的线程,虽然不能共享栈区数据,但是可以共享全局数据区和堆区数据,因此在子线程退出的时候可以将传出数据存储到全局变量、静态变量或者堆内存中。在下面的例子中将数据存储到了全局变量中:

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

// 定义结构
struct Persion
{
    int id;
    char name[36];
    int age;
};

struct Persion p;	// 定义全局变量

// 子线程的处理代码
void* working(void* arg)
{
    printf("我是子线程, 线程ID: %ld\n", pthread_self());
    for(int i=0; i<9; ++i)
    {
        printf("child == i: = %d\n", i);
        if(i == 6)
        {
            // 使用全局变量
            p.age  =12;
            strcpy(p.name, "tom");
            p.id = 100;
            // 该函数的参数将这个地址传递给了主线程的pthread_join()
            pthread_exit(&p);
        }
    }
    return NULL;
}

int main()
{
    // 1. 创建一个子线程
    pthread_t tid;
    pthread_create(&tid, NULL, working, NULL);

    printf("子线程创建成功, 线程ID: %ld\n", tid);
    // 2. 子线程不会执行下边的代码, 主线程执行
    printf("我是主线程, 线程ID: %ld\n", pthread_self());
    for(int i=0; i<3; ++i)
    {
        printf("i = %d\n", i);
    }

    // 阻塞等待子线程退出
    void* ptr = NULL;
    // ptr是一个传出参数, 在函数内部让这个指针指向一块有效内存
    // 这个内存地址就是pthread_exit() 参数指向的内存
    pthread_join(tid, &ptr);
    // 打印信息
    struct Persion* pp = (struct Persion*)ptr;
    printf("name: %s, age: %d, id: %d\n", pp->name, pp->age, pp->id);
    printf("子线程资源被成功回收...\n");
    
    return 0;
}


使用主线程栈
        虽然每个线程都有属于自己的栈区空间,但是位于同一个地址空间的多个线程是可以相互访问对方的栈空间上的数据的。由于很多情况下还需要在主线程中回收子线程资源,所以主线程一般都是最后退出,基于这个原因在下面的程序中将子线程返回的数据保存到了主线程的栈区内存中:

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

// 定义结构
struct Persion
{
    int id;
    char name[36];
    int age;
};


// 子线程的处理代码
void* working(void* arg)
{
    struct Persion* p = (struct Persion*)arg;
    printf("我是子线程, 线程ID: %ld\n", pthread_self());
    for(int i=0; i<9; ++i)
    {
        printf("child == i: = %d\n", i);
        if(i == 6)
        {
            // 使用主线程的栈内存
            p->age  =12;
            strcpy(p->name, "tom");
            p->id = 100;
            // 该函数的参数将这个地址传递给了主线程的pthread_join()
            pthread_exit(p);
        }
    }
    return NULL;
}

int main()
{
    // 1. 创建一个子线程
    pthread_t tid;

    struct Persion p;
    // 主线程的栈内存传递给子线程
    pthread_create(&tid, NULL, working, &p);

    printf("子线程创建成功, 线程ID: %ld\n", tid);
    // 2. 子线程不会执行下边的代码, 主线程执行
    printf("我是主线程, 线程ID: %ld\n", pthread_self());
    for(int i=0; i<3; ++i)
    {
        printf("i = %d\n", i);
    }

    // 阻塞等待子线程退出
    void* ptr = NULL;
    // ptr是一个传出参数, 在函数内部让这个指针指向一块有效内存
    // 这个内存地址就是pthread_exit() 参数指向的内存
    pthread_join(tid, &ptr);
    // 打印信息
    printf("name: %s, age: %d, id: %d\n", p.name, p.age, p.id);
    printf("子线程资源被成功回收...\n");
    
    return 0;
}

        在上面的程序中,调用 pthread_create() 创建子线程,并将主线程中栈空间变量 p 的地址传递到了子线程中,在子线程中将要传递出的数据写入到了这块内存中。也就是说在程序的 main() 函数中,通过指针变量 ptr 或者通过结构体变量 p 都可以读出子线程传出的数据。

6.线程分离

        线程分离是指在创建的线程完成执行之后,将其资源回收并释放,而不需要其他线程对其进行等待。一般情况下,线程分离可以通过调用pthread_detach函数来实现。 

        线程分离会使得线程的状态变成“分离状态”,分离状态的线程资源(如线程栈内存等)会被系统回收。但要注意,只有在线程执行完毕或主线程调用pthread_join之前,才能调用pthread_detach函数来将线程分离。

        线程分离的好处是可以避免因为线程资源未及时回收而导致的内存泄漏问题。但需要注意,如果线程分离之后,再次对其进行操作可能会导致程序崩溃等问题,因此在进行线程分离之前需要仔细考虑。

#include <pthread.h>
// 参数就子线程的线程ID, 主线程就可以和这个子线程分离了
int pthread_detach(pthread_t thread);

7.线程取消


线程取消的意思就是在某些特定情况下在一个线程中杀死另一个线程。使用这个函数杀死一个线程需要分两步:

在线程 A 中调用线程取消函数 pthread_cancel,指定杀死线程 B,这时候线程 B 是死不了的
在线程 B 中进程一次系统调用(从用户区切换到内核区),否则线程 B 可以一直运行。

#include <pthread.h>
// 参数是子线程的线程ID
int pthread_cancel(pthread_t thread);

8.线程 ID 比较


        在 Linux 中线程 ID 本质就是一个无符号长整形,因此可以直接使用比较操作符比较两个线程的 ID,但是线程库是可以跨平台使用的,在某些平台上 pthread_t 可能不是一个单纯的整形,这中情况下比较两个线程的 ID 必须要使用比较函数,函数原型如下:

#include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);
//参数:t1 和 t2 是要比较的线程的线程 ID
//返回值:如果两个线程 ID 相等返回非 0 值,如果不相等返回 0


总结:本文讲述了什么是线程,以及在Linux系统下线程的相关操作

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/604571.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Java多线程进阶】常见的锁策略

前言 众所周知&#xff0c;拳击运动员是要分等级&#xff08;轻量级、重量级等等&#xff09;来参加比赛的&#xff0c;在 Java 多线程中 锁&#xff08;synchronized&#xff09; 也会根据锁的竞争程度来升级为相关“高等级”锁&#xff0c;为了更好的理解 synchronized 加锁机…

微信小程序node+vue医院挂号预约系统fun17

从而实现管理员后端&#xff1b;首页、个人中心、用户管理、专家管理、科室类型管理、职称类型管理、医院挂号管理、挂号信息管理、留言板管理、系统管理&#xff0c;专家后端&#xff1b;首页、个人中心、医院挂号管理、挂号信息管理、系统管理&#xff0c;用户前端&#xff1…

【Linux】网络基础+UDP网络套接字编程

只做自己喜欢做的事情&#xff0c;不被社会和时代裹挟着前进&#xff0c;是一件很奢侈的事。 文章目录 一、 网络基础1.局域网和广域网2.协议初识和网络协议分层&#xff08;TCP/IP四层模型&#xff09;3.MAC地址和IP地址&#xff08;子网掩码&#xff0c;路由表&#xff0c;I…

美国金融科技公司SoFi的增长难以持久,股价也将下跌

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 公司介绍 SoFi Technologies(SoFi)是一家来自美国的知名金融科技公司&#xff0c;自2011年成立以来&#xff0c;已成为领先的个人理财在线平台。SoFi为年轻的高收入客户提供多样化的产品和服务&#xff0c;包括学生和汽车贷…

如何在 Python 中使用断点调试

入门教程、案例源码、学习资料、读者群 请访问&#xff1a; python666.cn 实际上没人能一次就写出完美的代码&#xff0c;除了我。但是世界上只有一个我。 林纳斯托瓦兹&#xff08;Linux 之父&#xff09; 大家好&#xff0c;欢迎来到 Crossin的编程教室 &#xff01; 上面这段…

【CSS3系列】第二章 · CSS3 新增盒模型和背景属性

写在前面 Hello大家好&#xff0c; 我是【麟-小白】&#xff0c;一位软件工程专业的学生&#xff0c;喜好计算机知识。希望大家能够一起学习进步呀&#xff01;本人是一名在读大学生&#xff0c;专业水平有限&#xff0c;如发现错误或不足之处&#xff0c;请多多指正&#xff0…

大数据:数据表操作,分区表,分桶表,修改表,array,map, struct

大数据&#xff1a;数据表操作&#xff0c;分区表 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xff0c;oracle&a…

【能量算子】评估 EEG 中的瞬时能量:非负、频率加权能量算子(PythonMatlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

五种方法提升Midjourney的出图品质

本文基于B站UP主琥珀川Eric的《五种方法提升Midjourney出图品质》制作在此感谢大神的分享。 本文全面介绍以上五种提升Midjourney出图品质的方法&#xff0c;简单实用&#xff0c;马上就可以用上。Lets go&#xff01;&#xff01;&#xff01; 方法一 使用相机参数创建逼真的图…

windows系统编译的Qt程序转到国产化麒麟linux中编译

团队自研股票软件&#xff0c;关威信共总号&#xff1a;QStockView&#xff0c;下载 1.1 windows系统编译的Qt程序转到国产化麒麟linux中编译 &#xff08;1&#xff09;把Vs工程项目文件导入到Linux中 首先把vs的工程拷贝到linux里面&#xff08;可以用虚拟机的共享文件夹…

适配器模式的学习与使用

1、适配器模式的学习 当我们需要将一个类的接口转换成另一个客户端所期望的接口时&#xff0c;适配器模式&#xff08;Adapter Pattern&#xff09;可以派上用场。它允许不兼容的接口之间能够协同工作。   适配器模式属于结构型设计模式&#xff0c;它包含以下几个角色&#…

2、数据库:SQL Server部署 - 系统部署系列文章

对于微软的SQL Server的安装&#xff0c;以前已经有写过了&#xff0c;到了2022版本&#xff0c;安装没多大的改变&#xff0c;很多只需要少配置&#xff0c;然后直接下一步即可。现在是2023年了&#xff0c;SQL Server已经出到了2022版本&#xff0c;这篇博文就再次对SQL Serv…

chatgpt赋能python:Python列表按长度排序的方法

Python列表按长度排序的方法 在Python编程中&#xff0c;列表是最常用的数据结构之一。列表是一种可变的有序序列&#xff0c;可以包含任意类型的对象。有时候&#xff0c;我们需要对列表按照元素的长度进行排序。本文将介绍Python中列表按长度排序的两种方法。 方法一&#…

pytorch实战 -- 神经网络

softmax的基本概念 交叉熵损失函数 模型训练和预测 在训练好softmax回归模型后&#xff0c;给定任一样本特征&#xff0c;就可以预测每个输出类别的概率。通常&#xff0c;我们把预测概率最大的类别作为输出类别。如果它与真实类别&#xff08;标签&#xff09;一致&#xff0…

chatgpt赋能python:Python列表排序详解:从基础排序到高级算法

Python 列表排序详解&#xff1a;从基础排序到高级算法 在 Python 编程中&#xff0c;列表是常用的数据类型。列表的排序是其中重要的操作之一。Python 提供了多种方法来对列表进行排序&#xff0c;从简单的基础排序到高级的算法排序。在这篇文章中&#xff0c;我们将详细介绍…

找到 FSM 的区别序列、UIO 或特征集(W方法)

找到 FSM 的区别序列、UIO 或特征集(W方法) 1 简介 许多系统都是基于状态的&#xff1a;它们有一个更新的内部状态通过操作并影响行为。 在测试这样一个系统时&#xff0c;一个需要考虑状态。 这导致了一系列的语言&#xff0c;用于描述基于状态的规范和模型&#xff0c;这些可…

并发编程-系统学习篇

并发编程的掌握过程并不容易。 我相信为了解决这个问题&#xff0c;你也听别人总结过&#xff1a;并发编程的第 一原则&#xff0c; 那就是不要写并发程序 这个原则在我刚毕业的那几年曾经是行得通的&#xff0c;那个时候多核服务器还是一种奢侈品&#xff0c;系统的并发量也很…

沙盒不再高端,Windows11将自带沙盒让程序检测更方便

Windows 沙盒提供了轻型桌面环境&#xff0c;可以安全地在隔离状态下运行应用程序。 安装在 Windows 沙盒环境下的软件保持“沙盒”状态&#xff0c;并且与主机分开运行。 沙盒是临时的。 当关闭沙盒后&#xff0c;系统将删除所有软件和文件以及状态。 每次使用时&#xff0c;…

AWK常用用法

awk简介 awk其名称得自于它的创始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母。实际上 AWK 的确拥有自己的语言&#xff1a; AWK 程序设计语言 &#xff0c; 三位创建者已将它正式定义为“样式扫描和处理语言”。它允许您创建简短的程序&#xff0c;…

解决一个典型的商业案例研究任务

介绍 印尼的一家公司 Gojek 通过移动应用程序提供运输和物流、食品和购物、支付、日常需求、商业、新闻和娱乐等服务&#xff0c;对经济做出了超过70亿美元的贡献。 它拥有 90 万注册商户、超过 1.9 亿次应用下载以及超过 200 万名司机能够在120分钟内完成超过18万个订单。我们…