Linux系统编程——线程的学习

2023-09-20 17:31:26

学习参考博文:
Linux多线程编程初探

Linux系统编程学习相关博文

  1. Linux系统编程——文件编程的学习
  2. Linux系统编程——进程的学习
  3. Linux系统编程——进程间通信的学习
  4. Linux系统编程——网络编程的学习

一、概述

常规学习Linux系统编程的内容是复杂且繁多的,不推荐刚开始接触代码的朋友去学习,所以介绍Linux系统编程的目的主要是以应用开发为主。

1. 进程与线程的区别

  1. 进程——资源分配的最小单位,线程——程序执行的最小单位
  2. 在面向线程设计的系统中,进程本身不是基本运行单位,而是线程的容器。程序本身只是指令、数据及其组织形式的描述,进程才是程序 (那些指令和数据) 的真正运行实例。(程序是静态的,进程是动态的)
  3. 一个进程里边可以有多个线程。进程的所有信息对该进程的所有线程都是共享的,包括可执行的程序文本、程序的全局内存和堆内存、栈以及文件描述符。
  4. 进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。
  5. 总的来说:进程有独立的地址空间,线程没有单独的地址空间(同一进程内的线程共享进程的地址空间)。

2. 使用线程的理由

  1. 使用多线程的理由之一是和进程相比,它是一种非常"节俭"的多任务操作方式。我们知道,在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种"昂贵"的多任务工作方式。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。据统计,总的说来,一个进程的开销大约是一个线程开销的30倍左右,当然,在具体的系统上,这个数据可能会有较大的区别。
  2. 使用多线程的理由之二是线程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。当然,数据的共享也带来其他一些问题,有的变量不能同时被两个线程所修改,有的子程序中声明为static的数据更有可能给多线程程序带来灾难性的打击,这些正是编写多线程程序时最需要注意的地方。

3. 互斥量

  1. 互斥量(mutex)从本质上来说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁。对互斥量进行加锁后,任何其他试图再次对互斥量加锁的线程将会被阻塞直到当前线程释放该互斥锁。如果释放互斥锁时有多个线程阻塞,所有在该互斥锁上的阻塞线程都会变成可运行状态,第一个变为可运行状态的线程可以对互斥量加锁,其他线程将会看到互斥锁依然被锁住,只能回去等待它重新变为可用。在这种方式下,每次只有一个线程可以向前运行。
  2. 在设计时需要规定所有的线程必须遵守相同的数据访问规则。只有这样,互斥机制才能正常工作。操作系统并不会做数据访问的串行化。如果允许其中的某个线程在没有得到锁的情况下也可以访问共享资源,那么即使其它的线程在使用共享资源前都获取了锁,也还是会出现数据不一致的问题。
  3. 互斥变量用pthread_mutex_t数据类型表示。在使用互斥变量前必须对它进行初始化,可以把它置为常量PTHREAD_MUTEX_INITIALIZER(只对静态分配的互斥量),也可以通过调用pthread_mutex_init函数进行初始化。如果动态地分配互斥量(例如通过调用malloc函数),那么在释放内存前需要调用pthread_mutex_destroy。

4. 条件变量

  1. 条件变量是线程另一可用的同步机制。条件变量给多个线程提供了一个会合的场所。条件变量与互斥量一起使用时,允许线程以无竞争的方式等待特定的条件发生。
  2. 条件本身是由互斥量保护的。线程在改变条件状态前必须首先锁住互斥量,其他线程在获得互斥量之前不会察觉到这种改变,因为必须锁定互斥量以后才能计算条件。
  3. 条件变量使用之前必须首先初始化,pthread_cond_t数据类型代表的条件变量可以用两种方式进行初始化,可以把常量PTHREAD_COND_INITIALIZER赋给静态分配的条件变量,但是如果条件变量是动态分配的,可以使用pthread_cond_destroy函数对条件变量进行去除初始化(deinitialize)。

二、线程API

在Linux系统中,操作系统提供了一系列的API,详细看下图

1. 线程
创建		pthread_creat()
退出		pthread_exit()
等待		pthread_join()
获取ID		pthread_self()
2. 互斥锁
创建		pthread_mutex_init()
销毁		pthread_mutex_destroy()
加锁		pthread_mutex_lock()
解锁		pthread_mutex_unlock()
3. 条件
创建		pthread_cond_init()
销毁		pthread_cond_destroy()
触发		pthread_cond_signal()
广播		pthread_cond_broadcast()
等待		pthread_cond_wait() / pthread_cond_timewait()

三、API介绍

1. pthread_creat函数

#include <pthread.h>

int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);

1. 函数功能:创建线程
2. 形参说明:
tidp:线程ID
attr:线程属性。暂可以把它设置为NULL,以创建默认属性的线程。
start_rtn:新创建线程的运行函数
arg:函数运行传递的参数
3. 返回值:成功返回0,失败返回错误编号

2. pthread_exit函数

#include <pthread.h>

int pthread_exit(void *rval_ptr);

1. 函数功能:线程退出
2. 形参说明:
rival_ptr:退出时返回的数据

3. pthread_join函数

#include <pthread.h>

int pthread_join(pthread_t thread, void **rval_ptr);

1. 函数功能:调用这个函数的线程将一直阻塞,直到指定的线程调用pthread_exit、从启动例程中返回或者被取消。
2. 形参说明:
thread:线程ID
rival_ptr:线程退出时返回的数据
3. 返回值:成功返回0,失败返回错误编号

4. pthread_self函数

#include <pthread.h>

pthread_t pthread_self(void);

1. 函数功能:获取线程ID
2. 返回值:调用线程的ID

5. pthread_mutex_inti函数

#include <pthread.h>

int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);

1. 函数功能:创建互斥锁
2. 形参说明:
mutex:锁ID
attr:锁的属性。默认的属性初始化互斥量,只需把attr设置为NULL3. 返回值:成功返回0,失败返回错误编号

6. pthread_mutex_destroy函数

#include <pthread.h>

int pthread_mutex_destroy(pthread_mutex_t *mutex);

1. 函数功能:销毁互斥锁
2. 形参说明:
mutex:锁ID
3. 返回值:成功返回0,失败返回错误编号

7. pthread_mutex_lock函数

#include <pthread.h>

int pthread_mutex_lock(pthread_mutex_t *mutex);

1. 函数功能:加锁
2. 形参说明:
mutex:锁ID
3. 返回值:成功返回0,失败返回错误编号

8. pthread_mutex_unlock函数

#include <pthread.h>

int pthread_mutex_unlock(pthread_mutex_t *mutex);

1. 函数功能:解锁
2. 形参说明:
mutex:锁ID
3. 返回值:成功返回0,失败返回错误编号

9. pthread_cond_init函数

#include <pthread.h>

int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);

1. 函数功能:创建条件变量
2. 形参说明:
cond:条件ID
attr:条件属性。除非需要创建一个非默认属性的条件变量,否则该参数可以设置为NULL3. 返回值:成功返回0,失败返回错误编号

10. pthread_cond_destroy函数

#include <pthread.h>

int pthread_cond_destroy(pthread_cond_t *cond);

1. 函数功能:销毁条件变量
2. 形参说明:
cond:条件ID
3. 返回值:成功返回0,失败返回错误编号

11. pthread_cond_wait函数

#include <pthread.h>

int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);

1. 函数功能:等待条件为真
2. 形参说明:
cond:条件ID
mutex:锁ID
3. 返回值:成功返回0,失败返回错误编号

12. pthread_cond_signal函数

#include <pthread.h>

int pthread_cond_signal(pthread_cond_t *cond);

1. 函数功能:触发条件
2. 形参说明:
cond:条件ID
3. 返回值:成功返回0,失败返回错误编号

四、API的使用例子

1. pthread_creat、pthread_exit、pthread_join、pthread_self函数

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 
  5 #include <pthread.h>
  6 
  7 // 1. int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*    start_rtn)(void *), void *restrict arg);
  8 // 2. pthread_t pthread_self(void);
  9 // 3. int pthread_join(pthread_t thread, void **rval_ptr);
 10 // 4. int pthread_exit(void *rval_ptr);
 11 
 12 void *func1(void *arg)
 13 {
 14     static int data = 10;
 15 
 16     printf("t1: %ld\n", pthread_self()); //打印线程ID
 17     printf("t1: arg = %d\n", *((int *)arg)); //打印传递的参数
 18 
 19     pthread_exit((void *)(&data)); //线程退出并返回数据
 20 }
 21 
 22 int main()
 23 {
 24     int ret = 0;
 25     int param = 100;
 26     int *pret = NULL;
 27 
 28     pthread_t t1; //线程ID
 29 
 30     ret = pthread_create(&t1, NULL, func1, (void *)&param); //创建线程
 31 
 32     if(ret == 0){
 33         printf("main: %ld, pthread create success\n", pthread_self());
 34     }
 35 
 36     pthread_join(t1, (void **)(&pret)); //等待线程退出
 37 
 38     printf("main: pret = %d\n", *pret); //打印线程退出时反馈的的数据
 39 
 40     return 0;
 41 }

2. pthread_mutex_init、pthread_mutex_destory、pthread_mutex_lock、pthread_mutex_unlock函数

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 
  5 #include <pthread.h>
  6 
  7 // 1. int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restric    t attr);
  8 // 2. int pthread_mutex_destroy(pthread_mutex_t *mutex);
  9 // 3. int pthread_mutex_lock(pthread_mutex_t *mutex);
 10 // 4. int pthread_mutex_unlock(pthread_mutex_t *mutex);
 11 
 12 int g_data = 0;
 13 
 14 pthread_mutex_t mutex; //锁
 15 
 16 void *func1(void *arg)
 17 {
 18     printf("t1: %ld\n", pthread_self()); //打印线程ID
 19     printf("t1: arg = %d\n", *((int *)arg)); //打印传递过来的数据
 20 
 21     pthread_mutex_lock(&mutex); //加锁
 22 
 23     while(1){
 24         printf("t1: %d\n", g_data++);
 25 
 26         if(g_data == 3){
 27             printf("t1 quit===========================\n");
 28             pthread_mutex_unlock(&mutex); //当g_data = 3时解锁
 29             pthread_exit(NULL); //线程退出
 30         }
 31 
 32         sleep(1);
 33     }
 34 
 35 }
 36 
 37 void *func2(void *arg)
 38 {
 39     printf("t2: %ld\n", pthread_self());
 40     printf("t2: arg = %d\n", *((int *)arg));
 41 
 42     while(1){
 43         pthread_mutex_lock(&mutex); //加锁
 44 
 45         printf("t2: %d\n", g_data++);
 46 
 47         pthread_mutex_unlock(&mutex); //解锁
 48 
 49         sleep(1);
 50     }
 51 }
 52 
 53 int main()
 54 {
 55     int ret = 0;
 56     int param = 100;
 57 
 58     pthread_t t1;
 59     pthread_t t2;
 60 
 61     pthread_mutex_init(&mutex, NULL); //创建互斥锁
 62 
 63     ret = pthread_create(&t1, NULL, func1, (void *)&param); //创建线程1
 64 
 65     if(ret == 0){
 66         //printf("main: t1:%ld, pthread create success\n", pthread_self());
 67     }
 68 
 69     ret = pthread_create(&t2, NULL, func2, (void *)&param); //创建线程2
 70 
 71     if(ret == 0){
 72         //printf("main: t2:%ld, pthread create success\n", pthread_self());
 73     }
 74 
 75     pthread_join(t1, NULL); //等待线程1退出
 76     pthread_join(t2, NULL); //等待线程2退出
 77 
 78     pthread_mutex_destroy(&mutex); //销毁互斥锁
 79 
 80     return 0;
 81 }

3. pthread_cond_init、pthread_cond_destory、pthread_cond_wait、pthread_cond_signal函数

1. 动态初始化

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 
  5 #include <pthread.h>
  6 
  7 // 1. int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*    start_rtn)(void *), void *restrict arg);
  8 // 2. pthread_t pthread_self(void);
  9 // 3. int pthread_join(pthread_t thread, void **rval_ptr);
 10 // 4. int pthread_exit(void *rval_ptr);
 11 // 5. int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mut    exattr_t *res    trict attr);
 12 // 6. int pthread_mutex_destroy(pthread_mutex_t *mutex);
 13 // 7. int pthread_mutex_lock(pthread_mutex_t *mutex);
 14 // 8. int pthread_mutex_unlock(pthread_mutex_t *mutex);
 15 // 9. int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict at    tr);
 16 // 10. int pthread_cond_destroy(pthread_cond_t *cond);
 17 // 11. int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
 18 // 12. int pthread_cond_signal(pthread_cond_t *cond);
 19 
 20 int g_data = 0;
 21 
 22 pthread_cond_t cond; //条件变量
 23 pthread_mutex_t mutex; //锁
 24 
 25 void *func1(void *arg)
 26 {
 27     printf("t1: %ld\n", pthread_self()); //打印线程ID
 28     printf("t1: arg = %d\n", *((int *)arg)); //打印传递过来的数据
 29 
 30     while(1){
 31         pthread_cond_wait(&cond, &mutex); //等待条件为真
 32 
 33         printf("t1 run=========================\n");
 34         printf("t1: %d\n", g_data);
 35         g_data = 0;
 36 
 37         sleep(1);
 38     }
 39 }
 40 
 41 void *func2(void *arg)
 42 {
 43     printf("t2: %ld\n", pthread_self());
 44     printf("t2: arg = %d\n", *((int *)arg));
 45 
 46     while(1){
 47         pthread_mutex_lock(&mutex); //加锁
 48 
 49         printf("t2: %d\n", g_data++);
 50         if(g_data == 3){
 51             pthread_cond_signal(&cond); //当g_data = 3时,触发条件,唤醒等待该条件的线程
 52         }
 53 
 54         pthread_mutex_unlock(&mutex); //解锁
 55 
 56         sleep(1);
 57     }
 58 }
 59 
 60 int main()
 61 {
 62     int ret = 0;
 63     int param = 100;
 64 
 65     pthread_t t1;
 66     pthread_t t2;
 67 
 68     pthread_cond_init(&cond, NULL); //创建条件变量
 69     pthread_mutex_init(&mutex, NULL); //创建互斥锁
 70 
 71     ret = pthread_create(&t1, NULL, func1, (void *)&param); //创建线程1
 72 
 73     if(ret == 0){
 74         //printf("main: t1:%ld, pthread create success\n", pthread_self());
 75     }
 76 
 77     ret = pthread_create(&t2, NULL, func2, (void *)&param); //创建线程2
 78 
 79     if(ret == 0){
 80         //printf("main: t2:%ld, pthread create success\n", pthread_self());
 81     }
 82 
 83     pthread_join(t1, NULL); //等待线程1退出
 84     pthread_join(t2, NULL); //等待线程2退出
 85 
 86     pthread_cond_destroy(&cond); //销毁条件变量
 87     pthread_mutex_destroy(&mutex); //销毁互斥锁
 88 
 89     return 0;
 90 }

2. 静态初始化

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 
  5 #include <pthread.h>
  6 
  7 // 1. int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*    start_rtn)(void *), void *restrict arg);
  8 // 2. pthread_t pthread_self(void);
  9 // 3. int pthread_join(pthread_t thread, void **rval_ptr);
 10 // 4. int pthread_exit(void *rval_ptr);
 11 // 5. int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mut    exattr_t *res    trict attr);
 12 // 6. int pthread_mutex_destroy(pthread_mutex_t *mutex);
 13 // 7. int pthread_mutex_lock(pthread_mutex_t *mutex);
 14 // 8. int pthread_mutex_unlock(pthread_mutex_t *mutex);
 15 // 9. int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict at    tr);
 16 // 10. int pthread_cond_destroy(pthread_cond_t *cond);
 17 // 11. int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
 18 // 12. int pthread_cond_signal(pthread_cond_t *cond);
 19 
 20 int g_data = 0;
 21 
 22 pthread_cond_t cond = PTHREAD_COND_INITIALIZER; //初始化条件变量
 23 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //初始化互斥锁
 24 
 25 void *func1(void *arg)
 26 {
 27     printf("t1: %ld\n", pthread_self()); //打印线程ID
 28     printf("t1: arg = %d\n", *((int *)arg)); //打印传递过来的值
 29 
 30     while(1){
 31         pthread_cond_wait(&cond, &mutex); //等待条件为真
 32 
 33         printf("t1 run=========================\n");
 34         printf("t1: %d\n", g_data);
 35         g_data = 0;
 36 
 37         sleep(1);
 38     }
 39 }
 40 
 41 void *func2(void *arg)
 42 {
 43     printf("t2: %ld\n", pthread_self()); //打印线程ID
 44     printf("t2: arg = %d\n", *((int *)arg)); //打印传递过来的值
 45 
 46     while(1){
 47         pthread_mutex_lock(&mutex); //加锁
 48 
 49         printf("t2: %d\n", g_data++);
 50         if(g_data == 3){
 51             pthread_cond_signal(&cond); //当g_data = 3时,触发条件,唤醒等待该条件的线程
 52         }
 53 
 54         pthread_mutex_unlock(&mutex); //解锁
 55 
 56         sleep(1);
 57     }
 58 }
 59 
 60 int main()
 61 {
 62     int ret = 0;
 63     int param = 100;
 64 
 65     pthread_t t1;
 66     pthread_t t2;
 67 
 68     //pthread_cond_init(&cond, NULL);
 69     //pthread_mutex_init(&mutex, NULL);
 70 
 71     ret = pthread_create(&t1, NULL, func1, (void *)&param); //创建线程1
 72 
 73     if(ret == 0){
 74         //printf("main: t1:%ld, pthread create success\n", pthread_self());
 75     }
 76 
 77     ret = pthread_create(&t2, NULL, func2, (void *)&param); //创建线程2
 78 
 79     if(ret == 0){
 80         //printf("main: t2:%ld, pthread create success\n", pthread_self());
 81     }
 82 
 83     pthread_join(t1, NULL); //等待线程1退出
 84     pthread_join(t2, NULL); //等待线程2退出
 85 
 86     pthread_cond_destroy(&cond); //销毁条件变量
 87     pthread_mutex_destroy(&mutex); //销毁互斥锁
 88 
 89     return 0;
 90 }
更多推荐

解决Nacos配置刷新导致定时器停止执行的问题

1.问题描述我使用了一个定时器类来执行某个任务,并且使用Nacos作为配置中心来管理定时器的配置。我发现当Nacos配置发生变化时,定时器实例会停止执行任务,导致任务无法按预期执行。2.原先的实现方式以下是我原先的代码实现方式:@Component@RefreshScope@RequiredArgsConstructo

英飞凌TC3xx--深度手撕HSM安全启动(四)--TC3xx HSM使能和配置技巧

上一章,我们简单聊了下英飞凌TC3xx的HSM的系统框架、相关UCB、Host和HSM通信模块。今天着重分析HSM的使能。1.系统引入HSM的思考为什么要增加HSM信息安全方面考虑,系统的安全启动、ECU之间安全数据的交互、ECU内部的敏感信息保存TC3xx使能HSM后,HSM的代码应该存放在哪里?在上一章,我们了解到

C++的移动构造和移动赋值运算符

右值引用右值引用(rvaluereferences)是一种新的用于绑定右值的引用类型。那么什么是右值?右值通常是编译器生成的用于表达式计算的临时变量或常量。目前来说,我们还不能安全地使用引用变量来绑定右值。从编译原理上讲,右值是只存在于表达式计算时的未命名值。下面这一表达式产生了一个右值:x+(y*z);//AC++e

SpringBoot携带Jre绿色部署项目_免安装Jdk[Linux服务器]

文章目录SpringBoot携带Jre绿色部署项目[Linux服务器]1.实现步骤2.自测成功,如下2-1环境准备2-2运行项目SpringBoot携带Jre绿色部署项目[Linux服务器]说明:实际应用的不方便场景:1.实际项目部属时,现有服务器可能已安装有Jdk,和自己项目的Jdk版本不一致,不敢轻易安装自己使用的

解决Permission is not allowed后基于Ubuntu23.04安装配置docker与docker-compose

参考:Docker官网-InstallDockerEngineonUbuntu虚拟机里安装ubuntu-23.04-beta-desktop-amd64,开启SSH(换源、备份),配置中文以及中文输入法等基于CentOS7安装配置docker与docker-compose一、InstallusingtheAptrepo

代码随想录算法训练营第二天(C) | 977.有序数组的平方 209.长度最小的子数组 59.螺旋矩阵

文章目录前言一、977.有序数组的平方二、209.长度最小的子数组三、59.螺旋矩阵总结前言java版:代码随想录算法训练营第二天|977.有序数组的平方,209.长度最小的子数组,59.螺旋矩阵_愚者__的博客-CSDN博客一、977.有序数组的平方双指针法:int*sortedSquares(int*nums,in

二进制 Deploy Kubernetes v1.23.17 超级详细部署

文章目录1.预备条件2.基础配置2.1配置root远程登录2.2配置主机名2.3安装ansible2.4配置互信2.5配置hosts文件2.6关闭防firewalld火墙2.7关闭selinux2.8关闭交换分区swap2.9修改内核参数2.10安装iptables2.11开启ipvs2.12配置limits参数2.1

nginx详解

目录1什么是nginx2Nginx功能2.1正向代理2.2反向代理2.3负载均衡2.4动静分离3区别4Docker安装nginx5nginx配置文件介绍1什么是nginxnginx是一个高性能的http和反向代理服务器,特点是占有内存少、并发能力强,事实上nginx的并发能力的确在同类的服务器中表现较好。nginx转为

聊一聊Twitter的雪花算法

什么是Twitter的雪花算法方法?这是一种在分布式系统中生成唯一ID的解决方案。Twitter在推文、私信、列表等方面使用这种方法。•ID是唯一且可排序的•ID包含时间信息(按日期排序)•ID适用于64位无符号整数•仅包含数字值符号位(1位):保留位(始终为0)。这可以为将来的请求保留。它可以潜在地用于使整体数字为正

vue柱状图+折线图组合

<template><divid="main"style="width:100%;height:500px;padding-top:.6rem"></div></template>data(){return{weekData:["1周","2周","3周","4周","5周","6周","7周","8周","9周","

控制台日志打印console的封装,加入美化、行显示与打印开关,支持node.js环境

控制台日志打印console的封装,加入美化、行显示与打印开关,支持node.js环境为什么要写这个?封装这个控制台日志打印工具,主要是在项目中自己做的SDK需要提供给其他开发人员使用,加入了日志美化和打印打开,方便了开发人员查找SDK中的日志(提高了逼格),也加深了自己对日志关键性的理解。首先讲下封装时遇到的一些问题

热文推荐