做房地产一级市场的看什么网站门户网站制作服务
- 作者: 五速梦信息网
- 时间: 2026年04月18日 10:02
当前位置: 首页 > news >正文
做房地产一级市场的看什么网站,门户网站制作服务,班级管理网站开发,上海高凡猎头公司GCD
使用GCD好处#xff0c;具体如下#xff1a;
GCD 可用于多核的并行运算#xff1b;GCD 会自动利用更多的 CPU 内核#xff08;比如双核、四核#xff09;#xff1b;GCD 会自动管理线程的生命周期#xff08;创建线程、调度任务、销毁线程#xff09;#xff1b…GCD
使用GCD好处具体如下
GCD 可用于多核的并行运算GCD 会自动利用更多的 CPU 内核比如双核、四核GCD 会自动管理线程的生命周期创建线程、调度任务、销毁线程程序员只需要告诉 GCD 想要执行什么任务不需要编写任何线程管理代码。
任务与队列
概念
任务就是执行操作的意思换句话说就是你在线程中执行的那段代码。在 GCD 中是放在 block 中的。执行任务有两种方式同步执行 和 异步执行。 两者的主要区别是是否等待队列的任务执行结束以及是否具备开启新线程的能力。 同步执行sync 同步添加任务到指定的队列中在添加的任务执行结束之前会一直等待直到队列里面的任务完成之后再继续执行。 只能在当前线程中执行任务不具备开启新线程的能力。 异步执行async 异步添加任务到指定的队列中它不会做任何等待可以继续执行任务。 可以在新的线程中执行任务具备开启新线程的能力。
注意异步执行async虽然具有开启新线程的能力但是并不一定开启新线程。这跟任务所指定的队列类型有关。 队列Dispatch Queue这里的队列指执行任务的等待队列即用来存放任务的队列。 队列是一种特殊的线性表采用 FIFO先进先出的原则即新任务总是被插入到队列的末尾而读取任务的时候总是从队列的头部开始读取。
GCD 提供了同步执行任务的创建方法dispatch_sync和异步执行任务创建方法dispatch_async
串行队列和并发队列
在 GCD 中有两种队列串行队列 和 并发队列。 两者都符合 FIFO先进先出的原则。 两者的主要区别是执行顺序不同以及开启线程数不同。 串行队列Serial Dispatch Queue 每次只有一个任务被执行。让任务一个接着一个地执行。只开启一个线程一个任务执行完毕后再执行下一个任务 并发队列Concurrent Dispatch Queue 可以让多个任务并发同时执行。可以开启多个线程并且同时执行任务
DISPATCH_QUEUE_SERIAL 表示串行队列。 DISPATCH_QUEUE_CONCURRENT 表示并发队列。
并发队列 的并发功能只有在异步dispatch_async方法下才有效。
主队列
主队列是一种特殊的 串行队列在libdispatch_init 初始化时就创建了主队列并且完成了与主线程的绑定。这些都是在程序main()函数之前就已完成的。 也就是说程序完成启动之时就已经有了主队列并且所有放在主队列中的任务都是在主线程中执行的。不管是同步还是异步都不会开辟新线程任务只会在主线程执行。这也是通常在主线程刷新UI时会将任务放到主队列的原因。 可通过dispatch_get_main_queue()获取主队列。
全局并发队列
全局并发队列 本质上是一个并发队列由系统提供方便编程不用创建就可使用。 可通过dispatch_get_global_queue(long indentifier.unsigned long flags)获取全局并发队列。 该函数提供了两个参数第一个参数表示队列优先级通常写0也就是默认优先级。可以通过服务质量类值来获取不同优先级的全局并发队列。
六种组合方式 同步执行并发队列
有如下代码
- (void)syncConcurrent {//打印syncConcurrent最开始执行的线程NSLog(%, [NSThread currentThread]);//使用CONCURRENT并发队列dispatch_queue_t queue dispatch_queue_create(elevenTest.queue, DISPATCH_QUEUE_CONCURRENT);dispatch_sync(queue, ^{[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(1–%, [NSThread currentThread]);});dispatch_sync(queue, ^{[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(2–%, [NSThread currentThread]);});dispatch_sync(queue, ^{[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(3–%, [NSThread currentThread]);}); }执行结果 所有任务都是在当前线程主线程中执行的没有开启新的线程同步执行不具备开启新线程的能力。 按顺序执行的原因虽然 并发队列 可以开启多个线程并且同时执行多个任务。 但是因为本身不能创建新线程只有当前线程这一个线程同步任务 不具备开启新线程的能力所以也就不存在并发。而且当前线程只有等待当前队列中正在执行的任务执行完毕之后才能继续接着执行下面的操作同步任务 需要等待队列的任务执行结束。 所以任务只能一个接一个按顺序执行不能同时被执行。 同步执行串行队列
- (void)syncConcurrent {//打印syncConcurrent最开始执行的线程NSLog(%, [NSThread currentThread]);//使用SERIAL串行队列dispatch_queue_t queue dispatch_queue_create(elevenTest.queue, DISPATCH_QUEUE_SERIAL);dispatch_sync(queue, ^{[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(1–%, [NSThread currentThread]);});dispatch_sync(queue, ^{[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(2–%, [NSThread currentThread]);});dispatch_sync(queue, ^{[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(3–%, [NSThread currentThread]);});
}运行结果
所有任务都是在当前线程主线程中执行的并没有开启新的线程同步执行 不具备开启新线程的能力。 任务是按顺序执行的串行队列 每次只有一个任务被执行任务一个接一个按顺序执行。 异步执行串行队列 - (void)syncConcurrent {//打印syncConcurrent最开始执行的线程NSLog(%, [NSThread currentThread]);NSLog(start);dispatch_queue_t queue dispatch_queue_create(elevenTest.queue1, DISPATCH_QUEUE_SERIAL);dispatch_async(queue, ^{[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(1–%, [NSThread currentThread]);});dispatch_async(queue, ^{[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(2–%, [NSThread currentThread]);});dispatch_async(queue, ^{[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(3–%, [NSThread currentThread]);});NSLog(end);
}执行结果
开启了一条新线程异步执行 具备开启新线程的能力串行队列 只开启一个线程。 任务是按顺序执行的串行队列 每次只有一个任务被执行任务一个接一个按顺序执行。 异步执行并发队列 - (void)syncConcurrent {//打印syncConcurrent最开始执行的线程NSLog(%, [NSThread currentThread]);NSLog(start);dispatch_queue_t queue dispatch_queue_create(elevenTest.queue1, DISPATCH_QUEUE_CONCURRENT);dispatch_async(queue, ^{[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(1–%, [NSThread currentThread]);});dispatch_async(queue, ^{[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(2–%, [NSThread currentThread]);});dispatch_async(queue, ^{[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(3–%, [NSThread currentThread]);});NSLog(end);
}执行结果
除了当前线程主线程系统又开启了 3 个线程并且任务是交替/同时执行的。异步执行 具备开启新线程的能力。且 并发队列 可开启多个线程同时执行多个任务。 说明当前线程没有等待而是直接开启了新线程在新线程中执行任务异步执行 不做等待可以继续执行任务。 同步执行主队列 - (void)syncConcurrent {//打印syncConcurrent最开始执行的线程NSLog(%, [NSThread currentThread]);NSLog(start);dispatch_queue_t queue dispatch_get_main_queue();dispatch_sync(queue, ^{[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(1–%, [NSThread currentThread]);});dispatch_sync(queue, ^{[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(2–%, [NSThread currentThread]);});dispatch_sync(queue, ^{[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(3–%, [NSThread currentThread]);});NSLog(end);
}执行结果
同步执行 主队列在主线程中调用会发生死锁问题而在其他线程中调用则不会。 这是因为我们在主线程中执行 syncConcurrent 方法相当于把 syncConcurrent 任务放到了主线程的队列中。而 同步执行 会等待当前队列中的任务执行完毕才会接着执行。那么当我们把 任务 1 追加到主队列中任务 1 就在等待主线程处理完 syncConcurrent 任务。而syncConcurrent 任务需要等待 任务 1 执行完毕才能接着执行。 那么现在的情况就是 syncConcurrent 任务和 任务 1 都在等对方执行完毕。这样大家互相等待所以就卡住了所以我们的任务执行不了而且 send 也没有打印。 为什么放到其他线程中就不会卡住了呢 因为当任务放到了其他线程里而 任务 1、任务 2、任务3 都在追加到主队列中这三个任务都会在主线程中执行。 syncConcurrent任务在其他线程中执行到追加 任务 1 到主队列中因为主队列现在没有正在执行的任务所以会直接执行主队列的 任务1等 任务1 执行完毕再接着执行 任务 2、任务 3。所以这里不会卡住线程也就不会造成死锁问题。 异步执行主队列 /*** 异步执行 主队列* 特点只在主线程中执行任务执行完一个任务再执行下一个任务*/ - (void)asyncMain {NSLog(currentThread—%,[NSThread currentThread]); NSLog(asyncMain—begin);dispatch_queue_t queue dispatch_get_main_queue();dispatch_async(queue, ^{// 追加任务 1[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(1—%,[NSThread currentThread]); // 打印当前线程});dispatch_async(queue, ^{// 追加任务 2[NSThread sleepForTimeInterval:2];// 模拟耗时操作NSLog(2—%,[NSThread currentThread]);// 打印当前线程});dispatch_async(queue, ^{// 追加任务 3[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(3—%,[NSThread currentThread]);// 打印当前线程});NSLog(asyncMain—end); }打印 [17521:4243690] currentThread—-{number 1, name main} [17521:4243690] asyncMain—-begin [17521:4243690] asyncMain—-end [17521:4243690] 1—-{number 1, name main} [17521:4243690] 2—-{number 1, name main} [17521:4243690] 3—-{number 1, name main}所有任务都是在当前线程主线程中执行的并没有开启新的线程虽然 异步执行 具备开启线程的能力但因为是主队列所以所有任务都在主线程中。 任务是按顺序执行的因为主队列是 串行队列每次只有一个任务被执行任务一个接一个按顺序执行。 为什么一定要在主线程更新UI呢 首先UIKit不是线程安全的当多个线程同时操作UI时抢夺资源有可能导致崩溃UI异常等问题。假如在两个线程中设置了同一张背景图片很有可能就会由于背景图片被释放两次使得程序崩溃。或者某一个线程中遍历找寻某个subView然而在另一个线程中删除了该subView那么就会造成错乱。 子线程到底能不能更新UI呢 有时也可以但是会有问题。在子线程能更新的UI是一个假象其实是子线程代码执行完毕了又自动进入到了主线程执行了子线程中的UI更新的函数栈这中间的时间非常的短就让大家误以为分线程可以更新UI。如果子线程一直在运行则子线程中的UI更新的函数栈主线程就无法获知那就无法更新直到子线程结束。 GCD的一些函数 栅栏函数dispatch_barrier_async/sync dispatch_barrier_async方法: 在调用 dispatch_barrier_async 之前所有被添加到并发队列中的任务都会并发执行。当 dispatch_barrier_async添加的任务开始执行时会等待之前所有添加到队列中的任务执行完毕。dispatch_barrier_async中添加的任务独占地执行确保在执行任务时没有其他任务在执行。栅栏中的任务执行完毕后队列恢复正常的并发行为后续添加的任务可以并发执行。 有如下代码
- (void)syncConcurrent {//打印syncConcurrent最开始执行的线程NSLog(%, [NSThread currentThread]);NSLog(start);dispatch_queue_t queue dispatch_queue_create(eleven, DISPATCH_QUEUE_CONCURRENT);dispatch_async(queue, ^{[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(1–%, [NSThread currentThread]);});dispatch_async(queue, ^{[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(2–%, [NSThread currentThread]);});dispatch_barrier_async(queue, ^{[NSThread sleepForTimeInterval:2];NSLog(barrier–%, [NSThread currentThread]);NSLog(barrier start);sleep(5);NSLog(barrier end);});dispatch_async(queue, ^{[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(3–%, [NSThread currentThread]);});dispatch_async(queue, ^{[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(4–%, [NSThread currentThread]);});NSLog(end);
}执行结果
dispatch_barrier_async不会阻塞调用它的线程屏障任务在并发队列中独占执行但调用它的线程可以继续执行其他任务。对于自定义的并发队列会按照上面的顺序执行但是如果是系统的全局并发队列dispatch_queue_t queue dispatch_get_global_queue(0, 0);就不是这个顺序了,他们四个任务的顺序是不确定的 dispatch_barrier_sync会阻塞调用它的线程直到屏障任务完成调用线程才会继续执行。 GCD 延时执行方法dispatch_after dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{// 2.0 秒后异步追加任务代码到主队列并开始执行NSLog(after—%,[NSThread currentThread]); });GCD 一次性代码只执行一次dispatch_once 我们在创建单例、或者有整个程序运行过程中只执行一次的代码时我们就用到了 GCD 的 dispatch_once 方法。 使用 dispatch_once 方法能保证某段代码在程序运行过程中只被执行 1 次并且即使在多线程的环境下dispatch_once 也可以保证线程安全。 - (void)once {static dispatch_once_t onceToken;dispatch_once(onceToken, ^{// 只执行 1 次的代码这里面默认是线程安全的}); }GCD 快速迭代方法dispatch_apply 通常我们会用 for 循环遍历但是 GCD 给我们提供了快速迭代的方法 dispatch_apply。 dispatch_apply 按照指定的次数将指定的任务追加到指定的队列中并等待全部队列执行结束。 如果是在串行队列中使用 dispatch_apply那么就和 for 循环一样按顺序同步执行。但是这样就体现不出快速迭代的意义了。 我们可以利用并发队列进行异步执行。比如说遍历 0~5 这 6 个数字for 循环的做法是每次取出一个元素逐个遍历。dispatch_apply 可以 在多个线程中同时异步遍历多个数字。 还有一点无论是在串行队列还是并发队列中dispatch_apply 都会等待全部任务执行完毕这点就像是同步操作也像是队列组中的 dispatch_group_wait方法。
- (void)syncConcurrent {dispatch_queue_t queue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);NSLog(apply—begin);dispatch_apply(6, queue, ^(size_t index) {NSLog(%zd—%,index, [NSThread currentThread]);});NSLog(apply—end);}执行结果
GCD 队列组dispatch_group 会有这样的需求分别异步执行2个耗时任务然后当2个耗时任务都执行完毕后再回到主线程执行任务。这时候我们可以用到 GCD 的队列组。 调用队列组的 dispatch_group_async先把任务放到队列中然后将队列放入队列组中。或者使用队列组的dispatch_group_enter、dispatch_group_leave组合来实现 dispatch_group_async。 调用队列组的dispatch_group_notify回到指定线程执行任务。或者使用dispatch_group_wait回到当前线程继续向下执行会阻塞当前线程。 dispatch_group_notify 监听 group 中任务的完成状态当所有的任务都执行完成后追加任务到 group 中并执行任务 - (void)viewDidLoad {[super viewDidLoad];//打印syncConcurrent最开始执行的线程NSLog(%, [NSThread currentThread]);NSLog(start);dispatch_group_t group dispatch_group_create();dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{[NSThread sleepForTimeInterval:2];NSLog(1–%, [NSThread currentThread]);});dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{[NSThread sleepForTimeInterval:2];NSLog(2–%, [NSThread currentThread]);});dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{[NSThread sleepForTimeInterval:2];NSLog(3–%, [NSThread currentThread]);});dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{[NSThread sleepForTimeInterval:2];NSLog(4–%, [NSThread currentThread]);});dispatch_group_notify(group, dispatch_get_main_queue(), ^{[NSThread sleepForTimeInterval:2];NSLog(5–%, [NSThread currentThread]);});dispatch_group_wait(group, DISPATCH_TIME_FOREVER);NSLog(end);
}执行结果
dispatch_group_wait 暂停当前线程阻塞当前线程等待指定的 group 中的任务执行完成后才会往下继续执行。 当所有任务执行完成之后才执行 dispatch_group_wait 之后的操作。但是使用dispatch_group_wait 会阻塞当前线程。 dispatch_group_enter、dispatch_group_leave dispatch_group_enter标志着一个任务追加到 group执行一次相当于 group 中未执行完毕任务数 1 dispatch_group_leave标志着一个任务离开了 group执行一次相当于 group 中未执行完毕任务数 -1。 当 group 中未执行完毕任务数为0的时候才会使 dispatch_group_wait 解除阻塞以及执行追加到 dispatch_group_notify 中的任务。 - (void)viewDidLoad {[super viewDidLoad];NSLog(currentThread—%,[NSThread currentThread]);NSLog(group—begin);dispatch_group_t group dispatch_group_create();dispatch_queue_t queue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_group_enter(group);dispatch_async(queue, ^{// 追加任务 1[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(1—%,[NSThread currentThread]); // 打印当前线程dispatch_group_leave(group);});dispatch_group_enter(group);dispatch_async(queue, ^{// 追加任务 2[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(2—%,[NSThread currentThread]);// 打印当前线程dispatch_group_leave(group);});dispatch_group_notify(group, dispatch_get_main_queue(), ^{// 等前面的异步操作都执行完毕后回到主线程.[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(3—%,[NSThread currentThread]);// 打印当前线程NSLog(group—end);}); }这里的dispatch_group_enter、dispatch_group_leave 组合其实等同于dispatch_group_async。 GCD 信号量dispatch_semaphore GCD 中的信号量是指 Dispatch Semaphore是持有计数的信号。 在 Dispatch Semaphore 中使用计数来完成这个功能计数小于 0 时等待不可通过。计数为 0 或大于 0 时计数减 1 且不等待可通过。 Dispatch Semaphore 提供了三个方法 dispatch_semaphore_create创建一个 Semaphore 并初始化信号的总量 dispatch_semaphore_signal发送一个信号让信号总量加 1 dispatch_semaphore_wait可以使总信号量减 1信号总量小于 0 时就会一直等待阻塞所在线程否则就可以正常执行。 注意信号量的使用前提是想清楚你需要处理哪个线程等待阻塞又要哪个线程继续执行然后使用信号量。 Dispatch Semaphore 在实际开发中主要用于 保持线程同步将异步执行任务转换为同步执行任务保证线程安全为线程加锁 但是使用信号量可能会造成线程优先级反转且无法避免。 有如下代码
- (void)viewDidLoad {[super viewDidLoad];NSLog(currentThread—%,[NSThread currentThread]);NSLog(semaphore—begin);dispatch_queue_t queue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_semaphore_t semaphore dispatch_semaphore_create(0);__block int number 0;dispatch_async(queue, ^{// 追加任务 1[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(1—%,[NSThread currentThread]);// 打印当前线程number 100;dispatch_semaphore_signal(semaphore);});dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);NSLog(semaphore—end,number %d,number);}执行结果
执行顺序如下 semaphore 初始创建时计数为 0。异步执行 将 任务 1 追加到队列之后不做等待接着执行 dispatch_semaphore_wait 方法semaphore 减 1此时 semaphore -1当前线程进入等待状态。然后异步任务 1 开始执行。任务 1 执行到 dispatch_semaphore_signal 之后总信号量加 1此时 semaphore 0正在被阻塞的线程主线程恢复继续执行。最后打印 semaphore—end,number 100。 线程同步可理解为线程 A 和 线程 B 一块配合A 执行到一定程度时要依靠线程 B 的某个结果于是停下来示意 B 运行B 依言执行再将结果给 AA 再继续操作。 线程池 线程池流程图如下
线程池Thread Pool是一种多线程管理机制用于提高应用程序的性能和资源利用率。线程池通过预先创建一定数量的线程并在需要执行任务时重复使用这些线程而不是每次都创建和销毁线程从而减少线程创建和销毁的开销。 通常使用GCDGrand Central Dispatch和NSOperationQueue来实现线程池的功能。 死锁问题 场景一 - (void)viewDidLoad {[super viewDidLoad];[self test1]; }- (void)test1 {NSLog(任务1——);dispatch_queue_t queue dispatch_get_main_queue();dispatch_sync(queue, ^{NSLog(任务2——);});NSLog(任务3——–); }主队列同步执行主队列是串行队列任务test1在主队列中在test1中同步执行了任务2。同步执行的队列中当前任务执行完之前会阻塞线程。而主线程每次只能执行一个任务因此test1任务要等待任务2执行完才能完成执行而任务2要等待test1执行完才能执行因此两个任务互相卡住了发生了死锁。 场景二
- (void)test2 {NSLog(任务1——);// 创建一个串行队列dispatch_queue_t queue dispatch_queue_create(com.longchi, DISPATCH_QUEUE_SERIAL);dispatch_async(queue, ^{ // 异步任务NSLog(任务2——);dispatch_sync(queue, ^{NSLog(任务3——);});NSLog(任务4——–);});NSLog(任务5——–); }在这段代码中queue是一个串行队列。向其中异步执行一个任务因为异步不会阻塞当前线程因此任务1和5都可以执行。在异步方法内又同步执行了一个方法又因为同步执行中的方法执行完成前会阻塞当前线程因此任务3执行完成前会一直阻塞线程但是这个同步方法是处于串行队列的因此要执行任务3要等待任务4执行完但是任务4又要等任务3执行完才能执行因此发生了死锁跟那个主队列同步执行差不多。
- 上一篇: 做房产信息网站河北省城乡建设厅网站
- 下一篇: 做房源网站高新区网站建设
相关文章
-
做房产信息网站河北省城乡建设厅网站
做房产信息网站河北省城乡建设厅网站
- 技术栈
- 2026年04月18日
-
做房产网站需要注意什么app购物网站建设
做房产网站需要注意什么app购物网站建设
- 技术栈
- 2026年04月18日
-
做房产网站需要了解什么上海 网站备案系统
做房产网站需要了解什么上海 网站备案系统
- 技术栈
- 2026年04月18日
-
做房源网站高新区网站建设
做房源网站高新区网站建设
- 技术栈
- 2026年04月18日
-
做非法网站怎么规避沧州网站设计多少钱
做非法网站怎么规避沧州网站设计多少钱
- 技术栈
- 2026年04月18日
-
做废钢那个网站好网站建设公司如何营销
做废钢那个网站好网站建设公司如何营销
- 技术栈
- 2026年04月18日
