C++多线程编程(一)(使用C++11后的thread类)

以下程序代码参考来源于
https://www.cnblogs.com/douzujun/p/10810506.html
https://www.cnblogs.com/douzujun/p/10816335.html
本篇只是自己学习的笔记,部分代码添加了自己理解的注释。

一、一个多线程程序示例,与单线程比较

//====================把一个复杂的计算拆分多线程实现的方式,与单线程做比较,可以看到多线程的好处==============
#include<iostream>
#include<thread>
#include<vector>
#include <chrono>
#include <cmath>
#include <cstdlib>
#include <future>
using namespace std;

double caculate(int v){
    if(v<=0){
        return v;
    }
    //假设这个计算过程很慢
    this_thread::sleep_for(chrono::milliseconds(10));
    return sqrt((v*v +sqrt((v-5)*(v*2.5))/2.0)/v);
}

template <typename Iter,typename Fun>
double visitRange(thread::id id,Iter iterBegin,Iter iterEnd,Fun func){
    auto curId = this_thread::get_id();//获取当前执行线程的ID
    if(id==this_thread::get_id()){
        cout<<curId<<"hello main thread\n";
    }else{
        cout<<curId<<"hello work thread\n";
    }
    double v=0;
    for(auto iter = iterBegin;iter!=iterEnd;++iter){
        v+=func(*iter);
    }
    return v;
}

int main(){
    auto mainThreadId = this_thread::get_id();

    //开启一个单线程
    vector<double> v;
    for(int i=0;i<1000;i++){
        v.push_back(rand());
    }
    cout<<v.size()<<endl;
    double value = 0.0;
    auto st = clock();  //需要包含什么头文件?
    for(auto & info:v){
        value += caculate(info);
    }
    auto ed = clock();
    cout<<"single thread: "<<value<<" "<<ed-st<<"time"<<endl;

    //下面使用多线程实现
    auto iterMid = v.begin() +(v.size()/2);
    //计算后半部分
    double anotherV = 0.0;
    auto iterEnd =v.end();
    st = clock();

    thread s([&anotherV,mainThreadId,iterMid,iterEnd](){
        anotherV = visitRange(mainThreadId,iterMid,iterEnd,caculate);
    });
    //计算前半部分
    auto halfv = visitRange(mainThreadId,v.begin(),iterMid,caculate);
    //关闭线程
    s.join();

    ed = clock();
    cout<<"multi thread: "<<(halfv+anotherV)<<" "<<ed-st <<"time"<<endl;

    return 0;
}

执行结果:

1000
single thread: 120540 10623time
1hello main thread
2hello work thread
multi thread: 120540 5343time

二、多线程遇到的问题

共享变量带来的问题:

#include <iostream>
#include <thread>
#include <vector>
#include <cstdlib>
using namespace std;

class Counter{
public:
    void addCount(){
        m_count++;
    }
    int count() const{ return m_count;} //使用const修饰成员函数,常用在返回值这里
    Counter():m_count(0){ } //提倡的初始化方式,值的初始化在构造函数执行
private:
    int m_count;
};
int work(int a){
    return a+a;
}

template <class Iter>
void realWork(Counter& c,double &totalValue,Iter b,Iter e){
    for(;b!=e;++b){
        totalValue +=work(*b);
        c.addCount(); //计数。
    }
}

int main(){
    unsigned n = thread::hardware_concurrency();  //检测硬件并发
    cout<<n<<" concurret thread are support.\n";

    //单线程实现
    vector<int> vec;
    double totalValue =0;
    for(int i=0;i<10000000;i++){
        vec.push_back(rand()%100);  //放入1~100的随机数
    }
    Counter counter;
    realWork(counter,totalValue,vec.begin(),vec.end());
    cout<<"total times: "<<counter.count()<<" "<<totalValue<<endl;

    //多线程实现
    totalValue =0;
    Counter counter2;
    auto iter = vec.begin()+(vec.size()/3);
    auto iter2 = vec.begin()+(vec.size()/3*2);
    //线程b负责计算1/3~2/3的工作
    thread b([&counter2,&totalValue,iter,iter2](){
        realWork(counter2,totalValue,iter,iter2);
    });
    auto end = vec.end();
    //线程c负责计算2/3~end的工作
    thread c([&counter2,&totalValue,iter2,end](){
        realWork(counter2,totalValue,iter2,end);
    });

    //main线程计算前1/3的工作
    realWork(counter2,totalValue,vec.begin(),iter);

    b.join();
    c.join();
    cout<<"total times use multithread: "<<counter2.count()<<" "<<totalValue<<endl;

    return 0;
}

执行结果:

4 concurret thread are support.
total times: 10000000 9.89328e+008
total times use multithread: 4833168 4.61286e+008

可以发现两次的运算的结果不一致,因为三个线程共享一份资源,出现了重复对同一个数值相加的情况,例如:main、b、c都
对m_count=6加了一次,结果最后结果只是7,这就是共享变量的问题所在

解决方法:
1、不共享变量totalvalue

#include <iostream>
#include <thread>
#include <vector>
#include <cstdlib>
using namespace std;

class Counter{
public:
    void addCount(){
        m_count++;
    }
    int count() const{ return m_count;} //使用const修饰成员函数,常用在返回值这里
    Counter():m_count(0){ } //提倡的初始化方式,值的初始化在构造函数执行
private:
    int m_count;
};
int work(int a){
    return a+a;
}
template <class Iter>
void realWork(Counter& c,double &totalValue,Iter b,Iter e){
    for(;b!=e;++b){
        totalValue +=work(*b);
        c.addCount(); //计数。
    }
}

int main(){
    unsigned n = thread::hardware_concurrency();  //检测硬件并发
    cout<<n<<" concurret thread are support.\n";

    //单线程实现
    vector<int> vec;
    double totalValue =0;
    for(int i=0;i<10000000;i++){
        vec.push_back(rand()%100);  //放入1~100的随机数
    }
    Counter counter;
    realWork(counter,totalValue,vec.begin(),vec.end());
    cout<<"total times: "<<counter.count()<<" "<<totalValue<<endl;

    //多线程实现
    totalValue =0;
    Counter counter2;
    auto iter = vec.begin()+(vec.size()/3);
    auto iter2 = vec.begin()+(vec.size()/3*2);
    //线程b负责计算1/3~2/3的工作
    double totalb = 0;   //每一个线程有自己的计数空间
    thread b([&counter2,&totalb,iter,iter2](){
        realWork(counter2,totalb,iter,iter2);
    });
    auto end = vec.end();
    //线程c负责计算2/3~end的工作
    double totalc = 0;
    thread c([&counter2,&totalc,iter2,end](){
        realWork(counter2,totalc,iter2,end);
    });

    //main线程计算前1/3的工作
    realWork(counter2,totalValue,vec.begin(),iter);

    b.join();
    c.join();
    cout<<"total times use multithread: "<<counter2.count()<<" "<<totalValue+totalb+totalc<<endl;

    return 0;
}

执行结果:

4 concurret thread are support.
total times: 10000000 9.89328e+008
total times use multithread: 4307691 9.89328e+008

2、添加原子性

方法2;原子操作变量类型(复杂,适合简单的应用)
/b,c 线程共享了变量 counter2, 没有共享变量 totalValue,所以totalValue一样,counter2.count()不一样
count++: 写入寄存器,寄存器+1,写入内存
average()函数功能是如果Counter2不等于10000000,程序就不退出,如运行截图,由于共享变量counter2, 导致counter2总是无法等于10000000
解决:原子操作变量
只需要把int m_count; 改成 atomic m_count; 即可
/

#include <iostream>
#include <thread>
#include <vector>
#include <cstdlib>
#include <string>
#include <atomic>  //添加头文件
using namespace std;

class Counter{
public:
    void addCount(){
        m_count++;
    }
    int count() const{ return m_count;} //使用const修饰成员函数,常用在返回值这里
    Counter():m_count(0){ } //提倡的初始化方式,值的初始化在构造函数执行
private:
//    int m_count;
    atomic<int> m_count;   //限制为原子操作
};
int work(int a){
    return a+a;
}
template <class Iter>
void realWork(Counter& c,double &totalValue,Iter b,Iter e){
    for(;b!=e;++b){
        totalValue +=work(*b);
        c.addCount(); //计数。
    }
}
bool average(Counter& c,int maxCount){
    auto cnt = c.count();
    if(cnt == maxCount){
        cout<<" ok finished\n";
        return true;
    }
    return false;
}

int main(){
    //(1)如果没有必要的话,线程间不要共享资源
    unsigned n = thread::hardware_concurrency();  //检测硬件并发
    cout<<n<<" concurret thread are support.\n";

//    单线程实现
    vector<int> vec;
    double totalValue =0;
    for(int i=0;i<10000000;i++){
        vec.push_back(rand()%100);  //放入1~100的随机数
    }
    Counter counter;
    realWork(counter,totalValue,vec.begin(),vec.end());
    cout<<"total times: "<<counter.count()<<" "<<totalValue<<endl;

//    多线程实现
    totalValue =0;
    Counter counter2;
    auto iter = vec.begin()+(vec.size()/3);
    auto iter2 = vec.begin()+(vec.size()/3*2);
//    b,c线程共享了变量counter2,没有共享totalvalue,所以最后结果totalvalue一样,counter不一样
    //线程b负责计算1/3~2/3的工作
    double totalb = 0;   //每一个线程有自己的计数空间
    thread b([&counter2,&totalb,iter,iter2](){
        realWork(counter2,totalb,iter,iter2);
    });
    auto end = vec.end();
    //线程c负责计算2/3~end的工作
    double totalc = 0;
    thread c([&counter2,&totalc,iter2,end](){
        realWork(counter2,totalc,iter2,end);
    });
    //main线程计算前1/3的工作
    realWork(counter2,totalValue,vec.begin(),iter);
    b.join();
    c.join();
    cout<<"total times use multithread: "<<counter2.count()<<" "<<totalValue+totalb+totalc<<endl;

    return 0;
}

执行结果:

 4 concurret thread are support.
total times: 10000000 9.89328e+008
total times use multithread: 10000000 9.89328e+008
//counter.count一致了

三、多线程操作多个变量

//=新增变量的处理,使用mutex互斥==========
核心部分:
void lockMutex() { m_mutex.lock(); }
void unlockMutex() { m_mutex.unlock(); }
使用:
c.lockMutex();
c.addCount();
c.addResource(1);
c.unlockMutex();
note:注意临界区的使用,否则可能会发生死锁

#include <thread>
#include <vector>
#include <cstdlib>
#include <string>
#include <atomic>
#include <mutex>
using namespace std;

class Counter{
public:
    void addCount(){
        m_count++;
    }
    int count() const{ return m_count;} //使用const修饰成员函数,常用在返回值这里
    Counter():m_count(0){ } //提倡的初始化方式,值的初始化在构造函数执行
    void addResource(int a){
        m_totalResource++;
    }
    int aveResource(){
        if(m_count==0)
            return 1;
        return m_totalResource/m_count;
    }
    void lockMutex(){ m_mutex.lock();}
    void unlockMutex(){ m_mutex.unlock();}
private:
//    int m_count;
    atomic<int> m_count;
    atomic<int> m_totalResource;
    mutex m_mutex;
};
int work(int a){
    return a+a;
}
template <class Iter>
void realWork(Counter& c,double &totalValue,Iter b,Iter e){
    for(;b!=e;++b){
        totalValue +=work(*b);
        c.lockMutex();
        c.addCount(); //计数。
        c.addResource(1);
        c.unlockMutex();
    }
}

bool average(Counter& c,int maxCount){
    auto cnt = c.count();

    //需要上锁的操作
    c.lockMutex();
    auto ave = c.aveResource();
    if(ave !=1) cout<<"has bad thing happened\n";
    c.unlockMutex();

    if(cnt == maxCount){
        cout<<" ok finished\n";
        return true;
    }
    return false;
}

int main(){
    //(1)如果没有必要的话,线程间不要共享资源
    unsigned n = thread::hardware_concurrency();  //检测硬件并发
    cout<<n<<" concurret thread are support.\n";

//    单线程实现
    vector<int> vec;
    double totalValue =0;
    for(int i=0;i<10000000;i++){
        vec.push_back(rand()%100);  //放入1~100的随机数
    }
    Counter counter;
    realWork(counter,totalValue,vec.begin(),vec.end());
    cout<<"total times: "<<counter.count()<<" "<<totalValue<<endl;

//    多线程实现
    totalValue =0;
    Counter counter2;
    thread printCount([&counter2](){
        while(!average(counter2,10000000)){ }
    });

    auto iter = vec.begin()+(vec.size()/3);
    auto iter2 = vec.begin()+(vec.size()/3*2);
//    b,c线程共享了变量counter2,没有共享totalvalue,所以最后结果totalvalue一样,counter不一样
    //线程b负责计算1/3~2/3的工作
    double totalb = 0;   //每一个线程有自己的计数空间
    thread b([&counter2,&totalb,iter,iter2](){
        realWork(counter2,totalb,iter,iter2);
    });
    auto end = vec.end();
    //线程c负责计算2/3~end的工作
    double totalc = 0;
    thread c([&counter2,&totalc,iter2,end](){
        realWork(counter2,totalc,iter2,end);
    });
    //main线程计算前1/3的工作
    realWork(counter2,totalValue,vec.begin(),iter);
    b.join();
    c.join();
//    cout<<"total times use multithread: "<<counter2.count()<<" "<<totalValue+totalb+totalc<<endl;

    auto realTotalCount = counter2.count();

    totalValue += totalb +totalc;
    cout<<"total times use multithread: "<<realTotalCount<<" "<<totalValue<<endl;

    printCount.join();

    return 0;
}
运行结果:
4 concurret thread are support.
total times: 10000000 9.89328e+008
 ok finished
total times use multithread: 10000000 9.89328e+008

四、把锁写到接口里

#include <iostream>
#include <thread>
#include <vector>
#include <cstdlib>
#include <string>
//#include <atomic>
#include <mutex>
using namespace std;

class Counter{
public:
    Counter():m_count(0),m_totalResource(0){ } //提倡的初始化方式,值的初始化在构造函数执行

    int count() const{  //添加const约束后,需要把mutex设置称为mutable类型
        m_mutex.lock();
        auto r =m_count;
        m_mutex.unlock();
        return r;
    }
    int aveResource(){
        m_mutex.lock();
        if(m_count==0){
            m_mutex.unlock();
            return 1;
        }
        auto r =m_totalResource/m_count;
        m_mutex.unlock();
        return r;
    }
    void addResourceAddCount(int r){
        m_mutex.lock();
        addCount();
        addResource(r);
        m_mutex.unlock();
    }

private:
//    int m_count;
//    atomic<int> m_count;
//    atomic<int> m_totalResource;
    void addResource(int a){
        m_totalResource++;
    }
    void addCount(){
        m_count++;
    }
    int m_count;
    int m_totalResource;
    mutable mutex m_mutex;
};
int work(int a){
    return a+a;
}
template <class Iter>
void realWork(Counter& c,double &totalValue,Iter b,Iter e){
    for(;b!=e;++b){
        totalValue +=work(*b);
        c.addResourceAddCount(1);
    }
}

bool average(Counter& c,int maxCount){
    auto cnt = c.count();

    auto ave = c.aveResource();
    if(ave !=1) cout<<"has bad thing happened\n";
    if(cnt == maxCount){
        cout<<" ok finished\n";
        return true;
    }
    return false;
}

int main(){
 //(1)如果没有必要的话,线程间不要共享资源
    unsigned n = thread::hardware_concurrency();  //检测硬件并发
    cout<<n<<" concurret thread are support.\n";

//    单线程实现
    vector<int> vec;
    double totalValue =0;
    for(int i=0;i<10000000;i++){
        vec.push_back(rand()%100);  //放入1~100的随机数
    }
    Counter counter;
    realWork(counter,totalValue,vec.begin(),vec.end());
    cout<<"total times: "<<counter.count()<<" "<<totalValue<<endl;

//    多线程实现
    totalValue =0;
    Counter counter2;
    thread printCount([&counter2](){
        while(!average(counter2,10000000)){ }
    });

    auto iter = vec.begin()+(vec.size()/3);
    auto iter2 = vec.begin()+(vec.size()/3*2);
//    b,c线程共享了变量counter2,没有共享totalvalue,所以最后结果totalvalue一样,counter不一样
    //线程b负责计算1/3~2/3的工作
    double totalb = 0;   //每一个线程有自己的计数空间
    thread b([&counter2,&totalb,iter,iter2](){
        realWork(counter2,totalb,iter,iter2);
    });
    auto end = vec.end();
    //线程c负责计算2/3~end的工作
    double totalc = 0;
    thread c([&counter2,&totalc,iter2,end](){
        realWork(counter2,totalc,iter2,end);
    });
    //main线程计算前1/3的工作
    realWork(counter2,totalValue,vec.begin(),iter);
    b.join();
    c.join();
//    cout<<"total times use multithread: "<<counter2.count()<<" "<<totalValue+totalb+totalc<<endl;

    auto realTotalCount = counter2.count();

    totalValue += totalb +totalc;
    cout<<"total times use multithread: "<<realTotalCount<<" "<<totalValue<<endl;

    printCount.join();

    return 0;
}
运行结果:
4 concurret thread are support.
total times: 10000000 9.89328e+008
 ok finished
total times use multithread: 10000000 9.89328e+008

五、自定义锁

原理:利用类对象使用完成后会自动调用析构函数,把上锁操作放在lock的构造函数中
释放锁操作放在lock的析构函数中,我们只用管定义,通过隐式调用构造和析构函数实现
我们想要的操作。

include <iostream>
#include <thread>
#include <vector>
#include <cstdlib>
#include <string>
//#include <atomic>
#include <mutex>
using namespace std;

template <typename T>
class Lock{
public:
    Lock(T& mutex):m_mutex(mutex){
        m_mutex.lock();
    }
    ~Lock(){
        m_mutex.unlock();
    }
private:
    T& m_mutex;
};

class Counter{
public:
    Counter():m_count(0),m_totalResource(0){ } //提倡的初始化方式,值的初始化在构造函数执行

    int count() const{  //添加const约束后,需要把mutex设置称为mutable类型
        Lock<mutex> lock(m_mutex);
        return m_count;
    }
    int aveResource(){
        Lock<mutex> lock(m_mutex);
        if(m_count==0){
            return 1;
        }
        return m_totalResource/m_count;
    }
    void addResourceAddCount(int r){
        Lock<mutex> lock(m_mutex);
        addCount();
        addResource(r);
//        m_mutex.unlock();  //为什么要这一句?不用的,博主的代码有错
    }
private:
//    int m_count;
//    atomic<int> m_count;
//    atomic<int> m_totalResource;
    void addResource(int a){
        m_totalResource++;
    }
    void addCount(){
        m_count++;
    }
    int m_count;
    int m_totalResource;
    mutable mutex m_mutex;
};
int work(int a){
    return a+a;
}
template <class Iter>
void realWork(Counter& c,double &totalValue,Iter b,Iter e){
    for(;b!=e;++b){
        totalValue +=work(*b);
        c.addResourceAddCount(1);
    }
}

bool average(Counter& c,int maxCount){
    auto cnt = c.count();

    auto ave = c.aveResource();
    if(ave !=1) cout<<"has bad thing happened\n";
    if(cnt == maxCount){
        cout<<" ok finished\n";
        return true;
    }
    return false;
}

int main(){
    //(1)如果没有必要的话,线程间不要共享资源
    unsigned n = thread::hardware_concurrency();  //检测硬件并发
    cout<<n<<" concurret thread are support.\n";

//    单线程实现
    vector<int> vec;
    double totalValue =0;
    for(int i=0;i<10000000;i++){
        vec.push_back(rand()%100);  //放入1~100的随机数
    }
    Counter counter;
    realWork(counter,totalValue,vec.begin(),vec.end());
    cout<<"total times: "<<counter.count()<<" "<<totalValue<<endl;

//    多线程实现
    totalValue =0;
    Counter counter2;
    thread printCount([&counter2](){
        while(!average(counter2,10000000)){ }
    });

    auto iter = vec.begin()+(vec.size()/3);
    auto iter2 = vec.begin()+(vec.size()/3*2);
//    b,c线程共享了变量counter2,没有共享totalvalue,所以最后结果totalvalue一样,counter不一样
    //线程b负责计算1/3~2/3的工作
    double totalb = 0;   //每一个线程有自己的计数空间
    thread b([&counter2,&totalb,iter,iter2](){
        realWork(counter2,totalb,iter,iter2);
    });
    auto end = vec.end();
    //线程c负责计算2/3~end的工作
    double totalc = 0;
    thread c([&counter2,&totalc,iter2,end](){
        realWork(counter2,totalc,iter2,end);
    });
    //main线程计算前1/3的工作
    realWork(counter2,totalValue,vec.begin(),iter);
    b.join();
    c.join();
//    cout<<"total times use multithread: "<<counter2.count()<<" "<<totalValue+totalb+totalc<<endl;

    auto realTotalCount = counter2.count();

    totalValue += totalb +totalc;
    cout<<"total times use multithread: "<<realTotalCount<<" "<<totalValue<<endl;

    printCount.join();

    return 0;
}
运行结果:
4 concurret thread are support.
total times: 10000000 9.89328e+008
 ok finished
total times use multithread: 10000000 9.89328e+008

六、使用STL的lock_guard

把上面自定义的lock换成lock_guard

#include <iostream>
#include <thread>
#include <vector>
#include <cstdlib>
#include <string>
//#include <atomic>
#include <mutex>
using namespace std;

class Counter{
public:
    Counter():m_count(0),m_totalResource(0){ } //提倡的初始化方式,值的初始化在构造函数执行

    int count() const{  //添加const约束后,需要把mutex设置称为mutable类型
        lock_guard<mutex> lock(m_mutex);
        return m_count;
    }
    int aveResource(){
        lock_guard<mutex> lock(m_mutex);
        if(m_count==0){
            return 1;
        }
        return m_totalResource/m_count;
    }
    void addResourceAddCount(int r){
        lock_guard<mutex> lock(m_mutex);
        addCount();
        addResource(r);
    }
private:
    void addResource(int a){
        m_totalResource++;
    }
    void addCount(){
        m_count++;
    }
    int m_count;
    int m_totalResource;
    mutable mutex m_mutex;
};
int work(int a){
    return a+a;
}
template <class Iter>
void realWork(Counter& c,double &totalValue,Iter b,Iter e){
    for(;b!=e;++b){
        totalValue +=work(*b);
        c.addResourceAddCount(1);
    }
}

bool average(Counter& c,int maxCount){
    auto cnt = c.count();

    auto ave = c.aveResource();
    if(ave !=1) cout<<"has bad thing happened\n";
    if(cnt == maxCount){
        cout<<" ok finished\n";
        return true;
    }
    return false;
}

int main(){
    //(1)如果没有必要的话,线程间不要共享资源
    unsigned n = thread::hardware_concurrency();  //检测硬件并发
    cout<<n<<" concurret thread are support.\n";

//    单线程实现
    vector<int> vec;
    double totalValue =0;
    for(int i=0;i<10000000;i++){
        vec.push_back(rand()%100);  //放入1~100的随机数
    }
    Counter counter;
    realWork(counter,totalValue,vec.begin(),vec.end());
    cout<<"total times: "<<counter.count()<<" "<<totalValue<<endl;

//    多线程实现
    totalValue =0;
    Counter counter2;
    thread printCount([&counter2](){
        while(!average(counter2,10000000)){ }
    });

    auto iter = vec.begin()+(vec.size()/3);
    auto iter2 = vec.begin()+(vec.size()/3*2);
//    b,c线程共享了变量counter2,没有共享totalvalue,所以最后结果totalvalue一样,counter不一样
    //线程b负责计算1/3~2/3的工作
    double totalb = 0;   //每一个线程有自己的计数空间
    thread b([&counter2,&totalb,iter,iter2](){
        realWork(counter2,totalb,iter,iter2);
    });
    auto end = vec.end();
    //线程c负责计算2/3~end的工作
    double totalc = 0;
    thread c([&counter2,&totalc,iter2,end](){
        realWork(counter2,totalc,iter2,end);
    });
    //main线程计算前1/3的工作
    realWork(counter2,totalValue,vec.begin(),iter);
    b.join();
    c.join();

    auto realTotalCount = counter2.count();
    totalValue += totalb +totalc;
    cout<<"total times use multithread: "<<realTotalCount<<" "<<totalValue<<endl;

    printCount.join();

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值