线程池

十二、连接池线程池配置——线程池 如果需要在 Spring Boot 中创建自定义线程池(例如用于特定的任务或服务),可以使用。 阅读详情

VS下线程池:http://www.cnblogs.com/kzloser/archive/2013/03/11/2909221.html

原文:http://blog.youkuaiyun.com/bravekingzhang/article/details/6953266

原文:http://blog.youkuaiyun.com/perfectpdl/article/details/6069473

       线程池不是什么时候都要用的,他也是一种资源,用的不对可能并不会带来性能上的提高,正确的使用方法是有一堆任务需要创建线程去执行,任务就是对象,在java里面,就好比是实现了Runnable接口的对象,那么此时就用线程池,如果紧紧就个吧两个进程,那得了,还是没必要,其实线程池,我看在效率上来讲,就是降低了线程创建的时间消耗,实际上原理和创建单个的线程差不太多。


  基本想法是这样的:
      1、预创建的线程通过mutex休眠在线程池中。这样,通过unlock该mutex就可以唤醒该线程了;
      2、出于简单性的目标,一个线程池内的所有线程的属性都是相同的。这个属性在创建线程池可以指定;
      3、一般来讲,线程池内的线程不能被取消、或者调用pthread_exit()退出。这些管理性工作是由线程池本身完成的。即,在使用线程池借出的线程时,函数返回应该只用return。
      4、从线程池“借出”的线程,可以归还给线程池。实际上也必须归还给线程池,这样线程池可以完成最后的清理工作。
      5、如果实在需要取消一个线程,那么好吧,只是别忘了告诉线程池你取消了它的手下。

#include "threadpool.h"        /* #include了所有必要的系统头文件 */

#define THWK_F_CLEAN    1    /* 设置此标志着threadpool正在进行清理操作,此时线程退出。 */
#define THWK_F_RUNNING    2    /* 设置这个标志主要是为了避免一个race condition,后述。 */

struct thread_worker_arg {
    void (*action)(void*);    /* user programmer指定的实际函数 */
    void *what;        /* action的参数 */
};

struct thread_worker {
    pthread_t id;            /* just as its name */
    struct thread_worker_arg arg;    /* 用于给sleepy_wrapper()传送参数,后述。 */
    pthread_mutex_t lock;        /* 用于实现线程池内空闲线程的休眠,它实际上并不保护什么临界区。 */
    struct thread_worker *next;    /* 用于链表线程池内的其他线程 */
    unsigned long long delay;    /* 未用,计划用于测量调度延迟。 */
    unsigned long flags;        /* 标志,后述。 */
};

struct thread_pool {
    pthread_mutex_t lock;        /* 用于同步对于thread_pool自身的访问操作 */
    struct thread_worker *first;    /* 所有线程链接于此 */
    int total;            /* 总线程数 */
    int current_nr;            /* 池内空闲线程数 */
};

/* 未用,计划用于测量调度延迟。 */
inline unsigned long long get_ticks(void) 
{
//    __asm__ ("rdtsc");

    return 0ULL;
}

/* 用于支持线程在被取消时的必要清理操作。 */
static void sleepy_wrapper_cleanup(void *voidp)
{
    struct thread_worker *worker = voidp;

    pthread_mutex_unlock(&worker->lock);
    free(worker);
}

/* 这就是线程池内线程的执行函数了。 */
static void* sleepy_wrapper(void *voidp)
{
    struct thread_worker *worker = voidp;

    while (1) {
        pthread_cleanup_push(sleepy_wrapper_cleanup, worker); /* 预设置上一个清理函数,防止线程取消时内存泄漏。 */
        pthread_mutex_lock(&worker->lock); /* 空闲线程应该休眠于此,这个mutex在创建thread pool时就锁住了。或者本循环结束时锁住。 */
        worker->delay = get_ticks() - worker->delay; /* 暂时无用。 */
        if (THWK_F_CLEAN & worker->flags) /* 线程池正在清理本身,所以线程至此就退出了。 */
            goto done; /* 你可能觉得这个goto用得有些多余,但如果不这样编译就会提示句法错误,因为pthread_cleanup_{push,pop}是用宏实现的!你可以参考一下它们的实现。 */
        worker->flags |= THWK_F_RUNNING; /* 后述。 */
        if (worker->arg.action) /* 进行线程实际的工作 */
            worker->arg.action(worker->arg.what);
done:
        pthread_mutex_unlock(&worker->lock); /* 解锁这个mutex,允许这个thread的下一次使用 */
        pthread_cleanup_pop(0);
        if (THWK_F_CLEAN & worker->flags) /* 清理线程池 */
            break;
        pthread_mutex_lock(&worker->lock); /* 先锁住这个锁,以让本循环开头的pthread_mutex_lock()使线程进入休眠。这个调用应该是成功的,否则就会引用deadlock。 */
        worker->flags &= ~THWK_F_RUNNING; /* 设计这个标志的意义在于防止有线程激活操作在以上unlock/lock之间发生,如果这样的话,就会引起deadlock,激活操作的实现后述。 */
    }
    pthread_exit(0);
}

/* 无需废话的函数。 */
pthread_t thread_pool_rawid(struct thread_worker *worker)
{
    return worker->id;
}

/* 如果线程被取消了,通知线程池忘记它,目前的实现很简单。*/
void thread_pool_forget(struct thread_pool *pool, struct thread_worker *worker)
{
    pool->total--;
} 

/* 线程激活操作 */
void thread_pool_activate(struct thread_worker *worker)
{
    worker->delay = get_ticks();
    while (thread_pool_is_running(worker)) /* 防止出现deadlock */
        ;
    pthread_mutex_unlock(&worker->lock); /* 使sleepy_wrapper()内循环开头部分的lock()操作返回,即线程得以唤醒执行实际的action(what)。 */
}

/* 另一个无须废话的函数 */
int thread_pool_is_running(struct thread_worker *worker)
{
    return (worker->flags & THWK_F_RUNNING);
}

/* 从线程池中借出一个线程,其实就是一个从链表头中摘出thread_worker的简单函数 */
int thread_pool_lend(struct thread_pool *pool, void (*action)(void*), void* what, struct thread_worker **worker)
{
    if (!action || !pool || !worker)
        return -EINVAL;

    pthread_mutex_lock(&pool->lock);
    *worker = pool->first;
    if (worker) {
        (*worker)->arg.action = action;
        (*worker)->arg.what = what;
        pool->first = (*worker)->next;
        (*worker)->next = NULL;
        pool->current_nr--;
    }
    pthread_mutex_unlock(&pool->lock);
    return 0;
}

/* 向线程池里归还一个thread,头插法插入thread_worker链表。 */
int thread_pool_giveback(struct thread_pool *pool, struct thread_worker *worker)
{
    if (!pool || !worker)
        return -EINVAL;

    while (thread_pool_is_running(worker))
        ;

    pthread_mutex_lock(&pool->lock);
    worker->next = pool->first;
    pool->first = worker;
    worker->arg.action = NULL;
    worker->arg.what = NULL;
    pool->current_nr++;
    pthread_mutex_unlock(&pool->lock);

    return 0;
}

/* 虽然有点长,但仍然是无须废话:线程池创建 */
struct thread_pool* thread_pool_create(int nr_to_create, pthread_attr_t *attr)
{
    struct thread_pool *pool;
    struct thread_worker *worker;
    int i, chk;

    if (!nr_to_create)
        return NULL;

    pool = malloc(sizeof(struct thread_pool));
    if (!pool)
        return NULL;

    pool->first = NULL;
    pool->total = 0;
    pthread_mutex_init(&pool->lock, NULL);

    for (i=0; i<nr_to_create; ++i) {
        worker = malloc(sizeof(struct thread_worker));
        if (!worker)
            break;
        memset(worker, 0, sizeof(struct thread_worker));

        pthread_mutex_init(&worker->lock, NULL);
        pthread_mutex_lock(&worker->lock);

        chk = pthread_create(&worker->id, attr, sleepy_wrapper, (void*)worker);
        if (chk) {
            pthread_mutex_unlock(&worker->lock);
            pthread_mutex_destroy(&worker->lock);
            free(worker);
            break;
        }
        worker->next = pool->first;
        pool->first = worker;
    }
    
    pool->total = i;
    pool->current_nr = i;
    if (0 == i) {
        pthread_mutex_destroy(&pool->lock);
        free(pool);
        pool = NULL;
    }
    return pool;
}

/* 清理线程池。 */
int thread_pool_clean(struct thread_pool *pool)
{
    struct thread_worker *worker;

    pthread_mutex_lock(&pool->lock);
    if (pool->total != pool->current_nr) {
        pthread_mutex_unlock(&pool->lock);
        return -EBUSY;
    }

    while (NULL != (worker = pool->first)) {
        worker->flags = THWK_F_CLEAN; /* this is =, rather than |= ! */
        pthread_mutex_unlock(&worker->lock);
        pthread_join(worker->id, NULL);
        pool->first = worker->next;
        pthread_mutex_destroy(&worker->lock);
        free(worker);
    }

    pthread_mutex_unlock(&pool->lock);
    pthread_mutex_destroy(&pool->lock);
    free(pool);
    return 0;
}

 

 

/* 这是一个使用例子。 */

/* 在我的P4双核机器上,可以比单线程版本快20%,但复杂性远高于20%! :( */

#include "threadpool.h"

unsigned long long sum(unsigned long long start, unsigned long long end)
{
    unsigned long long sum;

    sum = 0;
    for (; start<=end; ++start)
        sum += start;

    return sum;
}

struct per_sum {
    unsigned long long sum, start, end;
    pthread_mutex_t lock;
    pthread_cond_t cond;
};

void threaded_sum(void *voidp)
{
    struct per_sum *per_sum = voidp;

    printf("thread %p start/n", voidp);

    if (!per_sum) {
//        printf("per_sum == NULL/n");
        return;
    }
    per_sum->sum = sum(per_sum->start, per_sum->end);
    per_sum->start = per_sum->end = 0;
    pthread_mutex_lock(&per_sum->lock);
    printf("thread %p exit, end=%lld/n", voidp, per_sum->end);
    pthread_cond_signal(&per_sum->cond);
    pthread_mutex_unlock(&per_sum->lock);
}

int main(void)
{
#define NR_THREADS    2
    struct thread_worker* workers[NR_THREADS];
    struct per_sum per_sums[NR_THREADS];
    struct thread_pool *pool;
    int i;

    unsigned long long start, end;
    unsigned long long result = 0;
    unsigned long long delta = 0x10ffffff;

//    printf("mutli threading ... ");

    pool = thread_pool_create(NR_THREADS, NULL);
    if (!pool)
        exit(-1);

    for (i=0; i<NR_THREADS; ++i) {
        if (pthread_mutex_init(&per_sums[i].lock, NULL)) {
            printf("failed init mutex/n");
            exit(3);
        }            
        if (pthread_cond_init(&per_sums[i].cond, NULL)) {
            printf("failed init cond/n");
            exit(4);
        }        
        if (thread_pool_lend(pool, threaded_sum, (void*)&per_sums[i], &workers[i])) {
            printf("failed to lend thread %d/n", i);
            exit(5);
        } 
    }    

    start = 0;

    /* activate threads */
    for (i=0; i<NR_THREADS; i++) {
        per_sums[i].start = start;
        per_sums[i].end = per_sums[i].start + delta;
        start = per_sums[i].end + 1;
        thread_pool_activate(workers[i]);
    }

    for (i=0; i<NR_THREADS; i++) {
        pthread_mutex_lock(&per_sums[i].lock);
        while (per_sums[i].end != 0)
            pthread_cond_wait(&per_sums[i].cond, &per_sums[i].lock);
        result += per_sums[i].sum;
        pthread_mutex_unlock(&per_sums[i].lock);
    }

    /* activate threads again */
    for (i=0; i<NR_THREADS; i++) {
        per_sums[i].start = start;
        per_sums[i].end = per_sums[i].start + delta;
        start = per_sums[i].end + 1;
        thread_pool_activate(workers[i]);
    }
    end = per_sums[NR_THREADS-1].end;

    for (i=0; i<NR_THREADS; i++) {
        pthread_mutex_lock(&per_sums[i].lock);
        while (per_sums[i].end != 0)
            pthread_cond_wait(&per_sums[i].cond, &per_sums[i].lock);
        result += per_sums[i].sum;
        pthread_mutex_unlock(&per_sums[i].lock);
    }

    for (i=0; i<NR_THREADS; ++i) {
        if (thread_pool_giveback(pool, workers[i])) {
            printf("failed to giveback thread %d/n", i);
            exit(6);
        }
        pthread_mutex_destroy(&per_sums[i].lock);
        pthread_cond_destroy(&per_sums[i].cond);
    }

    thread_pool_clean(pool);
    printf("sum = %lld/n/n", result);
    return 0;
}

    PS:    是在Linux写的这个程序。完善的话,比如可以根据系统负载调整线程池中线程的数量;增加更完整的性能测量功能,调试功能;提供更方便的线程属性设置接口;在Linux平台上,可以使用clone()提供更为灵活的资源策略,等等。


怎么根据线程池告警修改线程池参数 调参通知、队列容量告警、拒绝策略告警必然是需要的 核心微服务: 必须加上 调参通知、队列容量告警、拒绝策略告警 如果是对于设备时效性要求较高=>加上 任务排队超时告警、任务执行超时告警 如果是长任务=>加上 任务执行超时告警、线程池活跃度告警。(线程池告警)该线程池的任务队列告警阈值需要降低阈值优化,如果是核心业务,则需要减少降低限度(如5%),直至可以在运维发现问题后,有足够时间让开发排查;可有可无的微服务,要避免浪费资源,也需要线程池监控查看配置,此时线程池情况可以做一个较好的缩容的指标。 阅读详情

相关推荐

深入线程池

像是服务员和厨师,要是都做请两个人把这两个任务一起做了,等同时来两个客人的时候,两人同时去服务客人,这时就没人做饭了,就是卡死在这一步,这就是一种饥饿。(ForkJoinP,它体现的是一种分治思想,适用于能够进行任务拆分的cpu密集型运算(Fork/Join默认创建cpu核心数相同的线程池。Fork/Join在分治的基础上加入了多线程,可以把每个任务的分解和合并交给不同的线程来完成,进一步提升了运算效率。整个线程池表现为线程数会根据任务量不断增长,没有上限,当任务执行完毕,空闲1min后释放线程。

2302_80742310的博客 1027

VS2013多线程

vs2013中配置多线程的使用环境 - qq_39907831的博客 - 优快云博客 https://blog.youkuaiyun.com/qq_39907831/article/details/78749465 【windows–进程和线程】多线程编程(比较完整的简单介绍) - 都江堰操作系统(DJYOS)-王建忠 - 优快云博客 https://blog.youkuaiyun.com/wangjianzhong...

蜕变之路的博客 2981

线程池详解:线程池七大核心参数、线程池工作原理、线程池的创建方式、线程池的拒绝策略、如何合理分配线程池大小

线程池是一种用于管理和复用线程的机制,它允许我们在需要执行一些并发任务时,可以重复使用已经创建好的线程,而不是每次都创建新的线程。这样可以减少线程创建和销毁的开销,提高系统的性能和响应速度。并发线程池的优点包括: 提高系统的性能和吞吐量,因为可以重复使用已经创建好的线程,减少线程创建和销毁的开销。 可以控制并发任务的执行顺序和数量,从而避免系统资源被过度占用。 可以设置线程池的参数,如核心线程数、最大线程数、任务队列等,以满足不同场景的需求。

小小鱼儿小小林的博客 1万+

200行C代码实现简单线程池【Linux】

原以为实现一个线程池一定是个规模和难度都是超大的项目。这阵子有些闲空,就复习了一下pthreads的知识。光理论复习不够的,还得上手写点东西才行。这就想着实现一个线程池试试看吧。没想到200行不到就出来了一个可用的东西。       基本想法是这样的:       1、预创建的线程通过mutex休眠在线程池中。这样,通过unlock该mutex就可以唤醒该线程了;       2、出于简单性的目标,一个线程池内的所有线程的属性都是相同的。这个属性在创建线程池可以指定;       3、一般来讲,线程

做一个优雅的程序员 1914

c++11线程与线程池Demo 完整vs2013工程

利用c++11标准库实现的线程和线程池样例程序,可调用任意参数的函数作为线程任务,支持类成员函数的调用。

C/C++(基于windows)超简单的线程池

1. 很适合新手学习线程池 2. 没有加锁, 大家要用的话自己加锁 3. 真的超简单, 代码量超少 4. 线程类是接口, 要自己继承才能使用, 里面有例子 5. 如果想加入 "插队" 线程机制, 可以把 STL的quene 换成 list, map等.

什么是线程池,线程池的概念、优点、缺点,如何使用线程池,最大线程池怎么定义?

线程池的优点包括重用线程、控制并发度、提供线程管理和监控等。通过适当地配置线程池的大小和任务队列的容量,可以充分利用系统资源,提高程序的性能和响应速度。同时,线程池可以避免线程创建和销毁的开销,减少了资源的浪费。线程池的基本概念是,在应用程序启动时创建一定数量的线程,并将它们保存在线程池中。当需要执行任务时,从线程池中获取一个空闲的线程,将任务分配给该线程执行。线程池的设计思想是为了避免频繁地创建和销毁线程的开销,以及控制并发执行的线程数量,从而提高系统的性能和资源利用率。

mcband的博客 4万+

线程池实现蚁群算法的简单并行

里面有几个文档是介绍相关背景,threadpool.h和threadpool.c文件包含了简单线程池的库函数,ant_pthread.c是并行蚁群算法的源代码

35.Java线程池(线程池概述、线程池的架构、线程池的种类与创建、线程池的底层原理、线程池的工作流程、线程池的拒绝策略、自定义线程池)

35.Java线程池(线程池概述、线程池的架构、线程池的种类与创建、线程池的底层原理、线程池的工作流程、线程池的拒绝策略、自定义线程池)

weixin_52173250的博客 1762

Miniforge3部署教程,完美替代Anaconda3

大背景不介绍了,哎!直接上教程!

zzy_ucas的博客 1万+

光电式烟雾传感器实战指南(一) 光迷宫设计与散射原理篇

本文深入解析了光电式烟雾传感器的核心部件——光迷宫的设计原理与光散射机制。文章详细阐述了如何通过精心设计的光学结构实现‘透气不透光’,并重点探讨了前向散射(尤其是135°角)在提升烟雾监测系统灵敏度与可靠性中的关键作用,为DIY爱好者提供了从理论到实践的完整指南。

weixin_29229055的博客 586

线程池怎么调优?线程池的几种方式?及线程池的实战

线程池的调优

weixin_60153382的博客 2701

米哈游 AI产品经理面试题精选:10道高频考题+答案解析(附PDF)

米哈游是全球知名游戏开发商,以《原神》《崩坏》系列著称,专注二次元文化与AI技术创新。本文整理了10道AI产品经理面试题,涵盖Transformer架构、智能NPC设计、机器学习应用、AIGC产品规划等核心领域。题目既考察技术理解(如RAG原理、多模态模型),又关注产品落地(游戏平衡性优化、伦理安全),同时强调跨团队协作能力。参考答案均结合米哈游业务场景,体现技术驱动与玩家体验并重的产品思维,展现AI在游戏行业的创新应用前景。

ZHHHHH15的博客 682

spring 的线程池还是 java 的线程池

Java 提供了一个强大的并发库,其中包含了线程池的基本实现。线程池是接口的实现类,用于管理线程的创建、执行和终止。通过线程池,开发者可以避免频繁创建和销毁线程带来的开销,从而提升应用程序的性能。Spring 框架对 Java 的并发库进行了封装,使得线程池的配置更加简洁和灵活。Spring 提供了接口,这是对 JavaExecutor接口的扩展,允许开发者更容易地配置和管理线程池。Spring 的线程池配置通常通过 XML 或 Java 配置类来实现,并且 Spring 提供了多种实现,如等。

Flying_Fish_roe的博客 1674

DeepSeek的训练与优化流程

DeepSeek的训练体系不仅追求极致的计算效率,更注重将物理规律、人类知识、安全伦理深度融入训练过程,其"效率-安全-可解释性"三角平衡的设计理念,为行业提供了重要参考范式。

m0_56896669的博客 1435

【华为鸿蒙3.0/荣耀安卓12使用VMOS Pro的激活方式 方法二】

关于荣耀手机安卓12如何解除进程限制,这里提供以下教程供小伙伴们参考。

Amy_mumu的博客 13万+

线程池——线程池

本文主要讲线程池:1)线程池的引入;2)Java标准库中的线程池;3)ThreadPoolExecutor 构造方法和拒绝策略;4)实现一个简单的线程池

2301_76937176的博客 1167

ue4 角色奔跑/冲刺/蹲下 /蹲走 动作切换蓝图

序列:文章分为4部分: 一、准备工作+简单的蓝图连线 二、编写“行走” 混合空间 三、编写“蹲下”混合空间 四、合成2个动画,设置动画蓝图 —————————————— 一、准备工作 下载“动画初学者包”,添加到项目! 主要文件: 打开BP_playerCharacter,组价——添加组件,找到“摄像机”,“机臂” 没有找到就去其他项目复制过来 ———————————— 手写人物的移动,也可以在...

凉城古梦猫扑街的博客 4862

Tomcat线程池监控及线程池原理分析

当有新任务时,Tomcat的线程池核心线程如果已经创建完了,Tomcat会尽最大努力开启新的非核心线程去执行新任务,而JUC的ThreadPoolExecutor则是入队,等待队列满了再创建新的非核心线程去执行任务。

wenyixicodedog的博客 1万+

华三模拟器实战:3分钟搞定路由器DHCP地址池配置(附常见错误排查)

本文提供华三模拟器中路由器DHCP地址池的实战配置指南。通过分步详解,帮助用户在3分钟内完成基础设置,并深入讲解多地址池、中继配置等进阶应用。文章重点剖析了DHCP服务无法工作的常见原因,提供从客户端到服务器的系统性排错方法,助力快速定位并解决地址分配失败、获取参数错误等典型问题。

w1x2y3的博客 360

RT-DETR改进策略【模型轻量化】| 替换骨干网络为 2024轻量化网络MoblieNetV4:移动生态系统的通用模型

本文记录的是基于MobileNet V4的RT-DETR目标检测轻量化改进方法研究。通过整合UIBMobile MQA以及优化的NAS策略能够在在不降低性能指标的前提下,降低计算成本。本文配置了原论文中和五种模型,以满足不同的需求。

Limiiiing的博客 1922

线程池讲解

/核心线程池大小//最大线程数//队列容量//活跃时间//线程名字前缀使用}, iAlgoFilter.getType() + "-----计算").start();}*/@Overridelog.info("当前线程:{},执行特征编号:{}的:{} 计算", Thread.currentThread().getName(),code, iAlgoFilter.getType());});

cf的博客 1664

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

线程池、及Springboot线程池实践

线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。

jainszhang的博客 1722

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

线程池详解,生命周期,线程池种类,预热

线程池详解,生命周期,线程池种类,预热

hello world 1315

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

【线程池】Tomcat线程池

最近看到篇关于 Tomcat 线程池的博客,因为之前只看过 JDK 线程池,记录一下。在微服务横行的今天,确实还是有必要研究研究 Tomcat 的线程池Tomcat 线程池和 JDK 线程池最大的不同就是它先把最大线程数用完,然后再提交任务到队列里面。深入理解线程池(ThreadPoolExecutor)——细致入微的讲源码。workerstarted = true-优快云博客本文主要介绍了 Tomcat 线程池的运行流程,和 JDK 线程池的流程比起来,它确实不一样。

m0_64944491的博客 1724
上一篇: HTTP 协议
下一篇: 线程清理处理函数 pthread_cleanup_push、pthread_cleanup_pop
labrary
博客等级 码龄15年 8粉丝 2原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值