鸿蒙ArkTS实战开发:TaskPool(2),HarmonyOS鸿蒙架构分为四层

}

let t: number = Date.now();
console.info("taskpool start time is: " + t);
let task: taskpool.Task = new taskpool.Task(printArgs, 100); // 100: test number
taskpool.executeDelayed(1000, task).then(() => { // 1000:delayTime is 1000ms
console.info(“taskpool execute success”);
}).catch((e: BusinessError) => {
console.error(taskpool execute: Code: ${e.code}, message: ${e.message});
})

taskpool.cancel

cancel(task: Task): void

取消任务池中的任务。当任务在taskpool等待队列中,取消该任务后该任务将不再执行,并返回undefined作为结果;当任务已经在taskpool工作线程执行,取消该任务并不影响任务继续执行,执行结果在catch分支返回,搭配isCanceled使用可以对任务取消行为作出响应。taskpool.cancel对其之前的taskpool.execute/taskpool.executeDelayed生效。

系统能力: SystemCapability.Utils.Lang

参数:

参数名类型必填说明
taskTask需要取消执行的任务。

错误码:

以下错误码的详细介绍请参见语言基础类库错误码

错误码ID错误信息
10200015The task does not exist when it is canceled.
10200016The task is executing when it is canceled.

从API version10开始,此接口调用时不再涉及上报错误码10200016。

正在执行的任务取消示例:

@Concurrent
function inspectStatus(arg: number): number {
// 第一次检查任务是否已经取消并作出响应
if (taskpool.Task.isCanceled()) {
console.info(“task has been canceled before 2s sleep.”);
return arg + 2;
}
// 2s sleep
let t: number = Date.now();
while (Date.now() - t < 2000) {
continue;
}
// 第二次检查任务是否已经取消并作出响应
if (taskpool.Task.isCanceled()) {
console.info(“task has been canceled after 2s sleep.”);
return arg + 3;
}
return arg + 1;
}

function concurrntFunc() {
let task1: taskpool.Task = new taskpool.Task(inspectStatus, 100); // 100: test number
let task2: taskpool.Task = new taskpool.Task(inspectStatus, 200); // 200: test number
let task3: taskpool.Task = new taskpool.Task(inspectStatus, 300); // 300: test number
let task4: taskpool.Task = new taskpool.Task(inspectStatus, 400); // 400: test number
let task5: taskpool.Task = new taskpool.Task(inspectStatus, 500); // 500: test number
let task6: taskpool.Task = new taskpool.Task(inspectStatus, 600); // 600: test number
taskpool.execute(task1).then((res: Object)=>{
console.info("taskpool test result: " + res);
});
taskpool.execute(task2);
taskpool.execute(task3);
taskpool.execute(task4);
taskpool.execute(task5);
taskpool.execute(task6);
// 1s后取消task
setTimeout(()=>{
try {
taskpool.cancel(task1);
} catch (e) {
console.error(taskpool: cancel error code: ${e.code}, info: ${e.message});
}
}, 1000);
}

concurrntFunc();

taskpool.cancel10+

cancel(group: TaskGroup): void

取消任务池中的任务组。当一个任务组的任务未全部执行结束时取消任务组,返回undefined作为任务组结果。

系统能力: SystemCapability.Utils.Lang

参数:

参数名类型必填说明
groupTaskGroup需要取消执行的任务组。

错误码:

以下错误码的详细介绍请参见语言基础类库错误码

错误码ID错误信息
10200018The task group does not exist when it is canceled.

示例:

@Concurrent
function printArgs(args: number): number {
let t: number = Date.now();
while (Date.now() - t < 2000) {
continue;
}
console.info("printArgs: " + args);
return args;
}

function concurrntFunc() {
let taskGroup1: taskpool.TaskGroup = new taskpool.TaskGroup();
taskGroup1.addTask(printArgs, 10); // 10: test number
let taskGroup2: taskpool.TaskGroup = new taskpool.TaskGroup();
taskGroup2.addTask(printArgs, 100); // 100: test number
taskpool.execute(taskGroup1).then((res: Array)=>{
console.info(“taskGroup1 res is:” + res);
});
taskpool.execute(taskGroup2).then((res: Array)=>{
console.info(“taskGroup2 res is:” + res);
});
setTimeout(()=>{
try {
taskpool.cancel(taskGroup2);
} catch (e) {
console.error(taskpool: cancel error code: ${e.code}, info: ${e.message});
}
}, 1000);
}

concurrntFunc();

taskpool.getTaskPoolInfo10+

getTaskPoolInfo(): TaskPoolInfo

获取任务池内部信息,包含线程信息和任务信息。

系统能力: SystemCapability.Utils.Lang

返回值:

类型说明
TaskPoolInfo任务池的内部信息。

示例:

let taskpoolInfo: taskpool.TaskPoolInfo = taskpool.getTaskPoolInfo();

Priority

表示所创建任务(Task)执行时的优先级。

系统能力: SystemCapability.Utils.Lang

名称说明
HIGH0任务为高优先级。
MEDIUM1任务为中优先级。
LOW2任务为低优先级。

示例:

@Concurrent
function printArgs(args: number): number {
let t: number = Date.now();
while (Date.now() - t < 1000) { // 1000: delay 1s
continue;
}
console.info("printArgs: " + args);
return args;
}

let allCount = 100; // 100: test number
let taskArray: Array<taskpool.Task> = [];
// 创建300个任务并添加至taskArray
for (let i: number = 1; i < allCount; i++) {
let task1: taskpool.Task = new taskpool.Task(printArgs, i);
taskArray.push(task1);
let task2: taskpool.Task = new taskpool.Task(printArgs, i * 10); // 10: test number
taskArray.push(task2);
let task3: taskpool.Task = new taskpool.Task(printArgs, i * 100); // 100: test number
taskArray.push(task3);
}

// 从taskArray中获取不同的任务并给定不同优先级执行
for (let i: number = 0; i < allCount; i+=3) { // 3: 每次执行3个任务,循环取任务时需后移3项,确保执行的是不同的任务
taskpool.execute(taskArray[i], taskpool.Priority.HIGH);
taskpool.execute(taskArray[i + 1], taskpool.Priority.LOW);
taskpool.execute(taskArray[i + 2], taskpool.Priority.MEDIUM);
}

Task

表示任务。使用constructor方法构造Task。任务可以多次执行或放入任务组执行或放入串行队列执行或添加依赖关系执行。

constructor

constructor(func: Function, …args: Object[])

Task的构造函数。

系统能力: SystemCapability.Utils.Lang

参数:

参数名类型必填说明
funcFunction执行的逻辑需要传入函数,必须使用@Concurrent装饰器装饰,支持的函数返回值类型请查序列化支持类型
argsObject[]任务执行传入函数的入参,支持的参数类型请查序列化支持类型。默认值为undefined。

错误码:

以下错误码的详细介绍请参见语言基础类库错误码

错误码ID错误信息
10200014The function is not mark as concurrent.

示例:

@Concurrent
function printArgs(args: number): number {
console.info("printArgs: " + args);
return args;
}

let task: taskpool.Task = new taskpool.Task(printArgs, “this is my first Task”);

constructor11+

constructor(name: string, func: Function, …args: Object[])

Task的构造函数,可以指定任务名称。

系统能力: SystemCapability.Utils.Lang

参数:

参数名类型必填说明
namestring任务名称。
funcFunction执行的逻辑需要传入函数,必须使用@Concurrent装饰器装饰,支持的函数返回值类型请查序列化支持类型
argsObject[]任务执行传入函数的入参,支持的参数类型请查序列化支持类型。默认值为undefined。

错误码:

以下错误码的详细介绍请参见语言基础类库错误码

错误码ID错误信息
10200014The function is not mark as concurrent.

示例:

@Concurrent
function printArgs(args: string): string {
console.info("printArgs: " + args);
return args;
}

let taskName: string = “taskName”;
let task: taskpool.Task = new taskpool.Task(taskName, printArgs, “this is my first Task”);
let name: string = task.name;

isCanceled10+

static isCanceled(): boolean

检查当前正在运行的任务是否已取消。使用该方法前需要先构造Task。

系统能力: SystemCapability.Utils.Lang

返回值:

类型说明
boolean如果当前正在运行的任务被取消返回true,未被取消返回false。

示例:

@Concurrent
function inspectStatus(arg: number): number {
// do something
if (taskpool.Task.isCanceled()) {
console.info(“task has been canceled.”);
// do something
return arg + 1;
}
// do something
return arg;
}

说明:
isCanceled方法需要和taskpool.cancel方法搭配使用,如果不调用cancel方法,isCanceled方法默认返回false。

示例:

@Concurrent
function inspectStatus(arg: number): number {
// 第一次检查任务是否已经取消并作出响应
if (taskpool.Task.isCanceled()) {
console.info(“task has been canceled before 2s sleep.”);
return arg + 2;
}
// 延时2s
let t: number = Date.now();
while (Date.now() - t < 2000) {
continue;
}
// 第二次检查任务是否已经取消并作出响应
if (taskpool.Task.isCanceled()) {
console.info(“task has been canceled after 2s sleep.”);
return arg + 3;
}
return arg + 1;
}

let task: taskpool.Task = new taskpool.Task(inspectStatus, 100); // 100: test number
taskpool.execute(task).then((res: Object)=>{
console.info("taskpool test result: " + res);
}).catch((err: string) => {
console.error("taskpool test occur error: " + err);
});
// 不调用cancel,isCanceled()默认返回false,task执行的结果为101

setTransferList10+

setTransferList(transfer?: ArrayBuffer[]): void

设置任务的传输列表。使用该方法前需要先构造Task。不调用该接口,则传给任务的数据中的ArrayBuffer默认transfer转移。

说明:
此接口可以设置任务池中ArrayBuffer的transfer列表,transfer列表中的ArrayBuffer对象在传输时不会复制buffer内容到工作线程而是转移buffer控制权至工作线程,传输后当前的ArrayBuffer失效。若ArrayBuffer为空,则不会transfer转移。

系统能力: SystemCapability.Utils.Lang

参数:

参数名类型必填说明
transferArrayBuffer[]可传输对象是ArrayBuffer的实例对象,默认为空数组。

错误码:

以下错误码的详细介绍请参见语言基础类库错误码

错误码ID错误信息
10200029Can not set an arraybuffer to both transferList and cloneList.

示例:

@Concurrent
function testTransfer(arg1: ArrayBuffer, arg2: ArrayBuffer): number {
console.info("testTransfer arg1 byteLength: " + arg1.byteLength);
console.info("testTransfer arg2 byteLength: " + arg2.byteLength);
return 100;
}

let buffer: ArrayBuffer = new ArrayBuffer(8);
let view: Uint8Array = new Uint8Array(buffer);
let buffer1: ArrayBuffer = new ArrayBuffer(16);
let view1: Uint8Array = new Uint8Array(buffer1);

console.info("testTransfer view byteLength: " + view.byteLength);
console.info("testTransfer view1 byteLength: " + view1.byteLength);
// 执行结果为:
// testTransfer view byteLength: 8
// testTransfer view1 byteLength: 16

let task: taskpool.Task = new taskpool.Task(testTransfer, view, view1);
task.setTransferList([view.buffer, view1.buffer]);
taskpool.execute(task).then((res: Object)=>{
console.info("test result: " + res);
}).catch((e: string)=>{
console.error("test catch: " + e);
})
console.info("testTransfer view2 byteLength: " + view.byteLength);
console.info("testTransfer view3 byteLength: " + view1.byteLength);
// 经过transfer转移之后值为0,执行结果为:
// testTransfer view2 byteLength: 0
// testTransfer view3 byteLength: 0

setCloneList11+

setCloneList(cloneList: Object[] | ArrayBuffer[]): void

设置任务的拷贝列表。使用该方法前需要先构造Task。

说明:
当前仅支持拷贝,@Sendable装饰器需搭配该接口使用,否则会抛异常。

系统能力: SystemCapability.Utils.Lang

参数:

参数名类型必填说明
cloneListObject[]ArrayBuffer[]

错误码:

以下错误码的详细介绍请参见语言基础类库错误码

错误码ID错误信息
10200029Can not set an arraybuffer to both transferList and cloneList.

示例:

import taskpool from ‘@ohos.taskpool’
import { BusinessError } from ‘@ohos.base’

@Sendable
class BaseClass {
private str: string = “sendable: BaseClass”;
static num :number = 10;
str1: string = “sendable: this is BaseClass’s string”;
num1: number = 5;
isDone1: boolean = false;

private fibonacciRecursive(n: number): number {
if (n <= 1) {
return n;
} else {
return this.fibonacciRecursive(n - 1) + this.fibonacciRecursive(n - 2);
}
}

private privateFunc(num: number): number{
let res: number = this.fibonacciRecursive(num);
console.info("sendable: BaseClass privateFunc res is: " + res);
return res;
}

publicFunc(num: number): number {
return this.privateFunc(num);
}

get GetNum(): number {
return this.num1;
}
set SetNum(num: number) {
this.num1 = num;
}

constructor(){
console.info(this.str);
this.isDone1 = true;
}
}

@Sendable
class DeriveClass extends BaseClass {
name: string = “sendable: this is DeriveClass”;
printName() {
console.info(this.name);
}
constructor() {
super();
}
}

@Concurrent
function testFunc(arr: Array, num: number): number {
let baseInstance1 = arr[0];
console.info("sendable: str1 is: " + baseInstance1.str1);
baseInstance1.SetNum = 100;
console.info("sendable: num1 is: " + baseInstance1.GetNum);
console.info("sendable: isDone1 is: " + baseInstance1.isDone1);
// 获取斐波那契数列第num项的结果
let res: number = baseInstance1.publicFunc(num);
return res;
}

@Concurrent
function printLog(arr: Array): void {
let deriveInstance = arr[0];
deriveInstance.printName();
}

@Entry
@Component
struct Index {
@State message: string = ‘Hello World’

build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
Button() {
Text(“TaskPool Test”)
}.onClick(() => {
// task1访问调用BaseClass.str1/BaseClass.SetNum/BaseClass.GetNum/BaseClass.isDone1/BaseClass.publicFunc
let baseInstance1: BaseClass = new BaseClass();
let array1 = new Array();
array1.push(baseInstance1);
let task1 = new taskpool.Task(testFunc, array1, 10);
task1.setCloneList(array1);
taskpool.execute(task1).then((res: Object) => {
console.info("sendable: task1 res is: " + res);
}).catch((e:BusinessError) => {
console.error(sendable: task1 execute Code is ${e.code}, message is ${e.message});
})

// task2调用DeriveClass.printName
let deriveInstance: DeriveClass = new DeriveClass();
let array2 = new Array();
array2.push(deriveInstance);
let task2 = new taskpool.Task(printLog, array2);
task2.setCloneList(array2);
taskpool.execute(task2).then(() => {
console.info(“sendable: task2 execute success”);
}).catch((e:BusinessError) => {
console.error(sendable: task2 execute Code is ${e.code}, message is ${e.message});
})
})
.height(‘15%’)
.width(‘30%’)
}
.width(‘100%’)
}
.height(‘100%’)
}
}

sendData11+

static sendData(…args: Object[]): void

在任务执行过程中向宿主线程发送消息并触发回调。使用该方法前需要先构造Task。

系统能力: SystemCapability.Utils.Lang

参数:

参数名类型必填说明
argsObject[]可传输对象默认转移,作为回调函数的参数,支持的参数类型请查序列化支持类型

错误码:

以下错误码的详细介绍请参见语言基础类库错误码

错误码ID错误信息
10200006An exception occurred during serialization.
10200022The function is not called in the taskpool thread.
10200023The function is not called in the concurrent function.
10200024The callback is not registered on the host side.

示例:

@Concurrent
function ConcurrentFunc(num: number): number {
let res: number = num * 10;
taskpool.Task.sendData(res);
return num;
}

onReceiveData11+

onReceiveData(callback?: Function): void

为任务注册回调函数,以接收和处理来自任务池工作线程的数据。使用该方法前需要先构造Task。

说明:
不支持给同一个任务定义多种回调函数,如果重复赋值只有最后一个会生效。

系统能力: SystemCapability.Utils.Lang

参数:

参数名类型必填说明
callbackFunction处理数据的回调函数,发送到宿主线程的数据将会作为入参传入该回调函数。不传参可以取消注册的回调函数。

示例:

@Concurrent
function ConcurrentFunc(num: number): number {
let res: number = num * 10;
taskpool.Task.sendData(res);
return num;
}

function pringLog(data: number): void {
console.info("taskpool: data is: " + data);
}

async function testFunc(): Promise {
try {
let task: taskpool.Task = new taskpool.Task(ConcurrentFunc, 1);
task.onReceiveData(pringLog);
await taskpool.execute(task);
} catch (e) {
console.error(taskpool: error code: ${e.code}, info: ${e.message});
}
}

testFunc();

addDependency11+

addDependency(…tasks: Task[]): void

为当前任务添加对其他任务的依赖。使用该方法前需要先构造Task。该任务和被依赖的任务不可以是任务组任务、串行队列任务和已执行的任务。存在依赖关系的任务(依赖其他任务的任务或被依赖的任务)执行后不可以再次执行。

系统能力: SystemCapability.Utils.Lang

参数:

参数名类型必填说明
tasksTask[]被依赖的任务数组。

错误码:

以下错误码的详细介绍请参见语言基础类库错误码

错误码ID错误信息
10200026There is a circular dependency.

示例:

@Concurrent
function delay(args: number): number {
let t: number = Date.now();
while ((Date.now() - t) < 1000) {
continue;
}
return args;
}

let task1:taskpool.Task = new taskpool.Task(delay, 100);
let task2:taskpool.Task = new taskpool.Task(delay, 200);
let task3:taskpool.Task = new taskpool.Task(delay, 200);

console.info(“dependency: add dependency start”);
task1.addDependency(task2);
task2.addDependency(task3);
console.info(“dependency: add dependency end”);

console.info(“dependency: start execute second”)
taskpool.execute(task1).then(() => {
console.info(“dependency: second task1 success”);
})
taskpool.execute(task2).then(() => {
console.info(“dependency: second task2 success”);
})
taskpool.execute(task3).then(() => {
console.info(“dependency: second task3 success”);
})

removeDependency11+

removeDependency(…tasks: Task[]): void

删除当前任务对其他任务的依赖。使用该方法前需要先构造Task。

系统能力: SystemCapability.Utils.Lang

参数:

参数名类型必填说明
tasksTask[]被依赖的任务数组。

错误码:

以下错误码的详细介绍请参见语言基础类库错误码

错误码ID错误信息
10200027The dependency does not exist.

示例:

@Concurrent
function delay(args: number): number {
let t: number = Date.now();
while ((Date.now() - t) < 1000) {
continue;
}
return args;
}

let task1:taskpool.Task = new taskpool.Task(delay, 100);
let task2:taskpool.Task = new taskpool.Task(delay, 200);
let task3:taskpool.Task = new taskpool.Task(delay, 200);

console.info(“dependency: add dependency start”);
task1.addDependency(task2);
task2.addDependency(task3);
console.info(“dependency: add dependency end”);
console.info(“dependency: remove dependency start”);
task1.removeDependency(task2);
task2.removeDependency(task3);
console.info(“dependency: remove dependency end”);

console.info(“dependency: start execute”)
taskpool.execute(task1).then(() => {
console.info(“dependency: task1 success”);
})
taskpool.execute(task2).then(() => {
console.info(“dependency: task2 success”);
})
taskpool.execute(task3).then(() => {
console.info(“dependency: task3 success”);
})

属性

系统能力: SystemCapability.Utils.Lang

名称类型可读可写说明
functionFunction创建任务时需要传入的函数,支持的函数返回值类型请查序列化支持类型
argumentsObject[]创建任务传入函数所需的参数,支持的参数类型请查序列化支持类型
name11+string创建任务时指定的任务名称。
totalDuration11+number执行任务总耗时。
ioDuration11+number执行任务异步IO耗时。
cpuDuration11+number执行任务CPU耗时。

TaskGroup10+

表示任务组,一次执行一组任务,适用于执行一组有关联的任务。如果所有任务正常执行,异步执行完毕后返回所有任务结果的数组,数组中元素的顺序与addTask的顺序相同;如果任意任务失败,则会抛出对应异常。任务组可以多次执行,但执行后不能新增任务。使用constructor方法构造TaskGroup。

constructor10+

constructor()

TaskGroup的构造函数。

系统能力: SystemCapability.Utils.Lang

示例:

let taskGroup = new taskpool.TaskGroup();

constructor11+

constructor(name: string)

TaskGroup的构造函数,可以指定任务组名称。

系统能力: SystemCapability.Utils.Lang

参数:

参数名类型必填说明
namestring任务组名称。

示例:

let taskGroupName: string = “groupName”;
let taskGroup: taskpool.TaskGroup = new taskpool.TaskGroup(taskGroupName);
let name: string = taskGroup.name;

addTask10+

addTask(func: Function, …args: Object[]): void

将待执行的函数添加到任务组中。使用该方法前需要先构造TaskGroup。

系统能力: SystemCapability.Utils.Lang

参数:

参数名类型必填说明
funcFunction执行的逻辑需要传入函数,必须使用@Concurrent装饰器装饰,支持的函数返回值类型请查序列化支持类型
argsObject[]任务执行函数所需要的入参,支持的参数类型请查序列化支持类型。默认值为undefined。

错误码:

以下错误码的详细介绍请参见语言基础类库错误码

错误码ID错误信息
10200014The function is not mark as concurrent.

示例:

@Concurrent
function printArgs(args: number): number {
console.info("printArgs: " + args);
return args;
}

let taskGroup: taskpool.TaskGroup = new taskpool.TaskGroup();
taskGroup.addTask(printArgs, 100); // 100: test number

addTask10+

addTask(task: Task): void

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数HarmonyOS鸿蒙开发工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年HarmonyOS鸿蒙开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上HarmonyOS鸿蒙开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新

如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注鸿蒙获取)
img

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • |
    | 10200014 | The function is not mark as concurrent. |

示例:

@Concurrent
function printArgs(args: number): number {
console.info("printArgs: " + args);
return args;
}

let taskGroup: taskpool.TaskGroup = new taskpool.TaskGroup();
taskGroup.addTask(printArgs, 100); // 100: test number

addTask10+

addTask(task: Task): void

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数HarmonyOS鸿蒙开发工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年HarmonyOS鸿蒙开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-YaJ8qKmo-1712806336533)]
[外链图片转存中…(img-h5iIbOtv-1712806336534)]
[外链图片转存中…(img-9S5Jc1N4-1712806336534)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上HarmonyOS鸿蒙开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新

如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注鸿蒙获取)
[外链图片转存中…(img-4EgBpDIC-1712806336535)]

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 17
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值