系统编程-----线程

线程

pthread_self():获取当前线程号

pthread_t pthread_self(void); 获取当前线程线程号
-lpthread.线程库没有在标准库中,所以加编译时动态加载库
main()函数创建的线程叫主线程:如果主线程结束,则其它线程会自动结束(无法运行)

pthread_create():创建子线程

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
@thread:用于存储新建线程号;
@att:线程属性;设置为NULL,使用系统默认属性创建线程,默认线程是联合态;
@start_routine:函数指针,线程实际要完成的任务;
@arg:是传给线程函数的数据;
union pthread_attr_t
{
char __size[__SIZEOF_PTHREAD_ATTR_T]; //字符数组:用于存储属性
long int __align; //字节对齐
};

pthread_join():线程等待

线程等待:主线程等待子线程
int pthread_join(pthread_t thread, void **retval); //相当于进程wait();
@thread:要等待的子线程线程号
@retval:接收子线程的退出状态

void pthread_exit():子线程退出;

子线程退出函数:
void pthread_exit(void *retval);
@retval:子线程的退出状态

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

void *pthread_fun_1(void *args)
{
    //获取子线程的线程号
    printf("%s start line = %d ,pthread_self = %lu\n",__func__,__LINE__,pthread_self());
    if(NULL != args)
    {
        int *p = (int *)args;
        printf("int arg = %d\n",*p);
    }
    printf("%s stop line = %d\n",__func__,__LINE__);

    //子线程退出
    pthread_exit((void *)0x05);
} 

int main()
{
    //获取主线程的线程号
    printf("%s line = %d ,pthread_self = %lu\n",__func__,__LINE__,pthread_self());

    //创建子线程
    pthread_t tid = 0;
    int data = 10;
    if(pthread_create(&tid,NULL,pthread_fun_1,&data) < 0)
    {
        perror("pthread_create error\n");
        return -1;
    }
#if 0 //等待方法一(不推荐使用)
    sleep(1);
#endif

#if 1 //等待方法二
    #if 0 //默认退出状态
    pthread_join(tid,NULL);//可以保证子线程顺利运行,NULL为默认为联合态
    #endif
    #if 1 //确定退出状态
    int *status = NULL;
    pthread_join(tid,(void **)&status);
    printf("status =%p\n",status);
    
    #endif
#endif
    printf("%s stop line = %d,pthread_self = %lu,tid = %lu\n",__func__,__LINE__,pthread_self(),tid);

  
}

线程分离

默认是联合态:在主线程pthread_join()过程中,如果接收到子线程的退出信号,则主线程立即回收子线程的堆栈空间
但是在做服务器时,因为主线程while(1),不会pthread_join(),则会造成子线程的堆栈空间无法立即回收;

线程主要属性:
Thread attributes:
Detach state = PTHREAD_CREATE_DETACHED
Scope = PTHREAD_SCOPE_SYSTEM
Inherit scheduler = PTHREAD_EXPLICIT_SCHED
Scheduling policy = SCHED_OTHER
Scheduling priority = 0
Guard size = 0 bytes
Stack address = 0x40197000
Stack size = 0x3000000 bytes

解决办法:线程分离:在分离态的子线程退出,由系统立即回收空间
分离办法一:在创建新线程时将线程属性设置为分离
1.声明线程属性变量:pthread_attr_t
2.初始化线程属性:
int pthread_attr_init(pthread_attr_t *attr);
3.将属性设置为分离属性:
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); 设置分离属性
@detachstate:
PTHREAD_CREATE_DETACHED 分离
PTHREAD_CREATE_JOINABLE 联合
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate); 获取分离属性
4.在线程结束时将属性销毁
int pthread_attr_destroy(pthread_attr_t *attr);
分离方法二:创建线程运行过程中设置为分离
int pthread_detach(pthread_t thread);
@thread:线程号

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

void *pthread_fun(void *args)
{
	printf("%s %d tid = %lu start\n",__func__,__LINE__,pthread_self());
	if(NULL != args)
	{
		int *p = (int *)args;
		printf("%s %d args = %d\n",__func__,__LINE__,*p);
	}

	printf("%s %d stop\n",__func__,__LINE__);
	pthread_exit((void *)0x5);
}


int main()
{
    //获取主线程的线程号
    printf("%s line = %d ,pthread_self = %lu\n",__func__,__LINE__,pthread_self());

    //创建子线程
    pthread_t tid = 0;
    int data = 10;
  

    //声明线程属性变量
    pthread_attr_t attr = {0};

    //初始化线程属性
    pthread_attr_init(&attr);

    //将属性设置为分离属性
    pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);

    if(pthread_create(&tid,NULL,pthread_fun,&data) < 0)
    {
        perror("pthread_create error\n");
        return -1;
    }

#if 0 //确定退出状态 等待 由主线程回收子线程的堆栈空间
    int *status = NULL;
    pthread_join(tid,(void **)&status);//使用地址值保存子线程的退出状态
    printf("status =%p\n",status);
#else //进行线程分离后 由系统回收 解决了在主线程while(1),不会pthread_join(),则会造成子线程的堆栈空间无法立即回收问题;
	int i;
	for(i = 0; i < 10; i++)
	{
		printf("main : pthread_self = %lu\n",pthread_self());
		sleep(1);
	}
 #endif   

    //在线程结束时销毁属性
    pthread_attr_destroy(&attr);

}

方法二

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

void *pthread_fun(void *args)
{
	printf("%s %d tid = %lu start\n",__func__,__LINE__,pthread_self());
	if(NULL != args)
	{
		int *p = (int *)args;
		printf("%s %d args = %d\n",__func__,__LINE__,*p);
	}

	printf("%s %d stop\n",__func__,__LINE__);
	pthread_exit((void *)0x5);
}


int main()
{
    //获取主线程的线程号
    printf("%s line = %d ,pthread_self = %lu\n",__func__,__LINE__,pthread_self());

    //创建子线程
    pthread_t tid = 0;
    int data = 10;
  
    //创建子线程
    if(pthread_create(&tid,NULL,pthread_fun,&data) < 0)
    {
        perror("pthread_create error\n");
        return -1;
    }
    //设置指定子线程为分离
    if(pthread_detach(tid) != 0)	
	{
		perror("ptread_detach error");
		return -1;
	}

#if 0 //确定退出状态 等待 由主线程回收子线程的堆栈空间
    int *status = NULL;
    pthread_join(tid,(void **)&status);
    printf("status =%p\n",status);
#else //进行线程分离后 由系统回收 解决了在主线程while(1),不会pthread_join(),则会造成子线程的堆栈空间无法立即回收问题;
	int i;
	for(i = 0; i < 10; i++)
	{
		printf("main : pthread_self = %lu\n",pthread_self());
		sleep(1);
	}
 #endif   

}

pthread_cleanup_push()、pthread_cleanup_pop():线程退出处理函数

注册线程退出处理函数:
void pthread_cleanup_push(void (*routine)(void *),void *arg);
@routine:退出处理函数:
@arg:传给退出处理函数的数据

退出处理函数的执行:
void pthread_cleanup_pop(int execute);
@execute:个数

#define pthread_cleanup_push(routine, arg)
do {
__pthread_cleanup_class __clframe (routine, arg)

/* Remove a cleanup handler installed by the matching pthread_cleanup_push.
If EXECUTE is non-zero, the handler function is called. */
#define pthread_cleanup_pop(execute) \ 所以一定要成对调用pthread_cleanup_pop(),否则编译出错;
__clframe.__setdoit (execute);
} while (0);

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


void pthread_exit_fun_1(void *args)
{
	printf("%s args = %p\n",__func__,args);
}

void pthread_exit_fun_2(void *args)
{
	printf("%s args = %p\n",__func__,args);
}

void *pthread_fun(void *args)
{
	printf("%s %d tid = %lu start\n",__func__,__LINE__,pthread_self());
	pthread_cleanup_push(pthread_exit_fun_1,args);	//注册
	pthread_cleanup_push(pthread_exit_fun_2,args + 1);

	if(NULL != args)
	{
		int *p = (int *)args;
		printf("%s %d args = %d\n",__func__,__LINE__,*p);
	}

	printf("%s %d stop\n",__func__,__LINE__);
#if 1
	pthread_cleanup_pop(1);	//执行
	pthread_cleanup_pop(1);
#endif
	pthread_exit(NULL);
	return NULL;
}

int main()
{
	printf("main start: pthread_self = %lu\n",pthread_self());
	pthread_t tid = 0;
	int data = 110;
	if(pthread_create(&tid,NULL,pthread_fun,&data) < 0)
	{
		perror("pthread_create error");
		return -1;
	}

	pthread_join(tid,NULL);	//使用地址值保存子线程的退出状态
	printf("main stop: pthread_self = %lu,tid = %lu\n",pthread_self(),tid);
}

创建多个子线程

创建多个子线程:多pthread_create()对应多个线程函数,也可对应同一个线程函数,pthread_join()多次
子线程间对于资源的使用是共享的,但是不共享线程的堆栈空间

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

static int data = 100;

void *pthread_fun1(void *args)
{
	printf("%s %d tid = %lu start\n",__func__,__LINE__,pthread_self());
	data += 5;
	printf("%s data = %d\n",__func__,data);
	printf("%s %d tid = %lu stop\n",__func__,__LINE__,pthread_self());
}

void *pthread_fun2(void *args)
{
	printf("%s %d tid = %lu start\n",__func__,__LINE__,pthread_self());
	data -= 6;
	printf("%s data = %d\n",__func__,data);
	printf("%s %d tid = %lu stop\n",__func__,__LINE__,pthread_self());
}

int main()
{
	printf("main start: pthread_self = %lu\n",pthread_self());
	pthread_t tid = 0,tid2 = 0;
	pthread_create(&tid,NULL,pthread_fun1,NULL);
	pthread_create(&tid2,NULL,pthread_fun2,NULL);
	pthread_join(tid,NULL);
	pthread_join(tid2,NULL);
	data += 2;
	printf("%s data = %d\n",__func__,data);
	printf("main stop: pthread_self = %lu,tid = %lu\n",pthread_self(),tid);
}

解决线程并发时的竞争方法一:互斥锁

解决线程并发时的竞争方法一:互斥锁
typedef union
{
struct __pthread_mutex_s
{
int __lock;
unsigned int __count;
int __owner;
int __kind;
} __data;
char __size[__SIZEOF_PTHREAD_MUTEX_T];
long int __align;
} pthread_mutex_t;

1.声明互斥锁:全局
pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER; //立即锁
pthread_mutex_t recmutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; //递归锁
pthread_mutex_t errchkmutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; //错误检查
2.初始化锁:创建线程之前main();
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
@mutex:互斥锁指针
@mutexatrr:互斥锁属性
3.加锁:线程函数中:操作数据前
int pthread_mutex_lock(pthread_mutex_t *mutex); //加阻塞锁 如果有线程已经加锁,则阻塞,直到另一线程解锁,才能加锁成功,继续运行
int pthread_mutex_trylock(pthread_mutex_t *mutex); //加非阻塞锁 非阻塞锁:加锁不成功不会阻塞,轮询
4.解锁:线程函数中:操作数据后
int pthread_mutex_unlock(pthread_mutex_t *mutex);
5.销毁锁:子线程运行结束时pthread_join()后
int pthread_mutex_destroy(pthread_mutex_t *mutex);

#include <pthread.h>
#include <stdio.h>

int main()
{
	pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
	int ret = pthread_mutex_init(&mutex,NULL);
	printf("pthread_mutex_init ret = %d\n",ret);

	ret = pthread_mutex_lock(&mutex);
	printf("pthread_mutex_lock ret = %d\n",ret);

	printf("data option\n");

	ret = pthread_mutex_unlock(&mutex);
	printf("pthread_mutex_unlock ret = %d\n",ret);

	ret = pthread_mutex_destroy(&mutex);
	printf("pthread_mutex_destroy ret = %d\n",ret);
}

使用互斥锁解决生产者消费者问题中的竞争问题

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

#if 0
使用互斥锁解决生产者消费者问题中的竞争问题
死锁:多进程或多线程并发时,对同一对共享资源产生了循环等待;
使用非阻塞锁解决死锁问题
#endif

static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static int pcount = 1000;

void *product_fun(void *args)
{
	printf("%s %d tid = %lu start\n",__func__,__LINE__,pthread_self());
	while(1)
	{
#if 0
		pthread_mutex_lock(&mutex);	//加阻塞锁
#endif
		if(pthread_mutex_trylock(&mutex))
		{
			sleep(1);
			continue;
		}

		pcount++;
		printf("%s %d tid = %lu pcount = %d\n",__func__,__LINE__,pthread_self(),pcount);
		pthread_mutex_unlock(&mutex);
	}
	printf("%s %d tid = %lu stop\n",__func__,__LINE__,pthread_self());
}

void *constom_fun(void *args)
{
	printf("%s %d tid = %lu start\n",__func__,__LINE__,pthread_self());
	while(1)
	{
#if 0
		pthread_mutex_lock(&mutex);	//加阻塞锁
#endif
		if(pthread_mutex_trylock(&mutex))
		{
			sleep(1);
			continue;
		}

		pcount--;
		printf("%s %d tid = %lu pcount = %d\n",__func__,__LINE__,pthread_self(),pcount);
		pthread_mutex_unlock(&mutex);
	}
	printf("%s %d tid = %lu stop\n",__func__,__LINE__,pthread_self());
}

int main()
{
	printf("main start: pthread_self = %lu\n",pthread_self());
	pthread_mutex_init(&mutex,NULL);
	pthread_t tid = 0,tid2 = 0;
	pthread_create(&tid,NULL,product_fun,NULL);
	pthread_create(&tid2,NULL,constom_fun,NULL);
	pthread_join(tid,NULL);
	pthread_join(tid2,NULL);
	pthread_mutex_destroy(&mutex);
	printf("main stop: pthread_self = %lu,tid = %lu\n",pthread_self(),tid);
}

条件变量使用步骤和相关函数

#include <stdio.h>
#include <pthread.h>
#include <errno.h>
#include <time.h>

条件变量使用步骤和相关函数:
1.声明条件变量(声明互斥锁)
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

2.初始化条件变量(初始化互斥锁)
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);

3.达到某条件时等待(当前线程调用):
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);	//阻塞等待
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime); //有超时的等待,会等待指定时间,如果在指定时间内有唤醒信号则唤醒,如果到达指定时间还没有唤醒信号则停止等待
struct timespec {
	__kernel_time_t	tv_sec;			/* seconds abs time*/
	long		tv_nsec;		/* nanoseconds */
};
成功返回0,如果超时返回ETIMEDOUT,

4.唤醒处理等待队列中的某线程(不能自己唤醒自己):
int pthread_cond_signal(pthread_cond_t *cond);	//只唤醒一个
int pthread_cond_broadcast(pthread_cond_t *cond); //唤醒所有

5.销毁条件变量(销毁互斥锁)
int pthread_cond_destroy(pthread_cond_t *cond);
条件变量的特性:
1.先等待后唤醒
2.只能当前线程唤醒其它线程,不能唤醒自己

int main()
{
	pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
	pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

	pthread_mutex_init(&mutex,NULL);
	int ret = pthread_cond_init(&cond,NULL);
	printf("pthread_cond_init ret = %d\n",ret);

#if 0
	ret = pthread_cond_wait(&cond,&mutex);
	printf("pthread_cond_wait ret = %d\n",ret);
#endif

#if 1
	struct timespec timsec = {0};
	timsec.tv_sec = time(NULL) + 3;
	ret = pthread_cond_timedwait(&cond,&mutex,&timsec);
	if(ret == 0)
	{
		printf("option data\n");
	}
	else if(ret == ETIMEDOUT)
	{
		printf("pthread_cond_timedwait timeout\n");
	}
	else 
		printf("other error\n");
#endif

	ret = pthread_cond_signal(&cond);
	printf("pthread_cond_signal ret = %d\n",ret);

	pthread_mutex_destroy(&mutex);
	ret = pthread_cond_destroy(&cond);
	printf("pthread_cond_destroy ret = %d\n",ret);
}

使用条件变量解决生产者消费者并发时的竞争问题

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

#if 0
使用条件变量解决生产者消费者并发时的竞争问题
#endif

static int pcount = 1000;
static char hasproduct = 0;	//是否有产品
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

void *product_fun(void *args)
{
	printf("%s %d tid = %lu start\n",__func__,__LINE__,pthread_self());
	while(1)
	{
		if(hasproduct)	//如果有产品就等待
			pthread_cond_wait(&cond,&mutex);
		else			//如果没有产品就生产
		{
			pcount++;
			printf("%s %d tid = %lu pcount = %d\n",__func__,__LINE__,pthread_self(),pcount);
			hasproduct = 1;	//生产后就有产品
			pthread_cond_signal(&cond);	//唤醒消费者消息
		}
	}
	printf("%s %d tid = %lu stop\n",__func__,__LINE__,pthread_self());
}

void *constom_fun(void *args)
{
	printf("%s %d tid = %lu start\n",__func__,__LINE__,pthread_self());
	while(1)
	{
		if(!hasproduct)		//如果没有产品就等待
			pthread_cond_wait(&cond,&mutex);
		else				//如果有产品就消费
		{
			pcount--;
			printf("%s %d tid = %lu pcount = %d\n",__func__,__LINE__,pthread_self(),pcount);
			hasproduct = 0;
			pthread_cond_signal(&cond);		//唤醒生产者生产
		}
	}
	printf("%s %d tid = %lu stop\n",__func__,__LINE__,pthread_self());
}

int main()
{
	printf("main start: pthread_self = %lu\n",pthread_self());
	pthread_mutex_init(&mutex,NULL);
	pthread_cond_init(&cond,NULL);
	pthread_t tid = 0,tid2 = 0;
	pthread_create(&tid,NULL,product_fun,NULL);
	pthread_create(&tid2,NULL,constom_fun,NULL);
	pthread_join(tid,NULL);
	pthread_join(tid2,NULL);
	pthread_mutex_destroy(&mutex);
	pthread_cond_destroy(&cond);
	printf("main stop: pthread_self = %lu,tid = %lu\n",pthread_self(),tid);
}

信号量解决线程并发

#include <stdio.h>
#include <semaphore.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>

#if 0
信号量:非负整数(同一时间能够访问共享资源的线程数)
typedef union
{
  char __size[__SIZEOF_SEM_T];
  long int __align;
} sem_t;

PV原语:
1.声明信号量:sem_t
2.初始化信号量:
int sem_init(sem_t *sem, int pshared, unsigned int value);
@pshared:0表示同一进程内的线程共享;
@value:初始化的信号量值:
3.p操作:
int sem_wait(sem_t *sem);	//阻塞版p操作
int sem_trywait(sem_t *sem);//非阻塞版p操作
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);	//带超时时间的p操作
4.v操作
int sem_post(sem_t *sem);
5.销毁
int sem_destroy(sem_t *sem);
6.获取信号量值
int sem_getvalue(sem_t *sem, int *sval); 
@sval:存储获取到的信号量值
#endif

int main()
{
	sem_t sem = {0};
	int ret = sem_init(&sem,0,1);
	int value = 0;
	ret = sem_getvalue(&sem,&value);
	printf("sem_getvalue ret = %d,value = %d\n",ret,value);

	ret = sem_wait(&sem);	//p操作

#if 0
	ret = sem_wait(&sem);	//p操作,阻塞版p操作
#endif

#if 0						//非阻塞p操作,轮询
	while((ret = sem_trywait(&sem)))
	{
		printf("sem_wait ret = %d\n",ret);
		sleep(1);
	}
#endif

#if 0
	struct timespec timsec = {0};
	timsec.tv_sec = time(NULL) + 5;
	ret = sem_timedwait(&sem,&timsec);
	if(ret == 0)
		printf("data opion");
	else if(ret == -1)
	{
		printf("timeout\n");
		sem_destroy(&sem);
		return -1;
	}
#endif	
	
	printf("sem_wait ret = %d\n",ret);
	ret = sem_getvalue(&sem,&value);
	printf("sem_getvalue ret = %d,value = %d\n",ret,value);

	printf("data option\n");

	ret = sem_post(&sem);	//v操作
	ret = sem_getvalue(&sem,&value);
	printf("sem_getvalue ret = %d,value = %d\n",ret,value);

	ret = sem_destroy(&sem);
	printf("ret = %d\n",ret);
}

使用信号量解决生产者消费者问题

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <semaphore.h>

#if 0
使用信号量解决生产者消费者问题:
1.解决并发(多个任务同时(同一时间点)运行):可以有多进程,也可以使用多线程;
1)线程占用资源少(进程有独立的.text等,多个线程共享同一进程的.text等);
2)线程响应时间短
3)线程间切换比进程切换快,效率高

2.并发时竞争问题解决:
#endif

static int pcount = 1000;
static sem_t sem = {0};

void *product_fun(void *args)
{
	printf("%s %d tid = %lu start\n",__func__,__LINE__,pthread_self());
	while(1)
	{
		sem_wait(&sem);
		pcount++;
		printf("%s %d tid = %lu pcount = %d\n",__func__,__LINE__,pthread_self(),pcount);
		sem_post(&sem);
	}
	printf("%s %d tid = %lu stop\n",__func__,__LINE__,pthread_self());
}

void *constom_fun(void *args)
{
	printf("%s %d tid = %lu start\n",__func__,__LINE__,pthread_self());
	while(1)
	{
		sem_wait(&sem);
		pcount--;
		printf("%s %d tid = %lu pcount = %d\n",__func__,__LINE__,pthread_self(),pcount);
		sem_post(&sem);
	}
	printf("%s %d tid = %lu stop\n",__func__,__LINE__,pthread_self());
}

int main()
{
	printf("main start: pthread_self = %lu\n",pthread_self());
	sem_init(&sem,0,1);
	pthread_t tid = 0,tid2 = 0;
	pthread_create(&tid,NULL,product_fun,NULL);
	pthread_create(&tid2,NULL,constom_fun,NULL);
	pthread_join(tid,NULL);
	pthread_join(tid2,NULL);
	sem_destroy(&sem);
	printf("main stop: pthread_self = %lu,tid = %lu\n",pthread_self(),tid);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值