C++并发与多线程(高级函数大全)

async

在 C++ 中,async 关键字用于实现异步编程,它允许你定义异步操作,这些操作可以在后台执行,而不会阻塞当前线程。这是 C++11 引入的特性,与 std::async 函数和 std::future 类一起使用。与thread函数模板的区别在于async可以有返回值,thread无返回值。

简单入门

// ConsoleApplication10.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <thread>
#include <mutex>
#include <cstdint>
#include <future>

#include <list>
using  namespace std;



int mythread()
{
	cout << "func " << "  threadid = " << this_thread::get_id() << " start " << endl << endl;
	std::chrono::milliseconds dura(3000);
	std::this_thread::sleep_for(dura);
	cout << "func " << "  threadid = " << this_thread::get_id() <<" end " << endl << endl;
	return 5;

}

int main()
{
	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
	std::future<int> ret =  std::async(mythread);
	cout << ret.get() << endl; //堵塞获取值,注意:get函数只能调用一次,不能多次调用
    //注意:如果不写get,程序会等子线程退出后在自行退出
	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;

	return 0;
}



中级过度

// ConsoleApplication10.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <thread>
#include <mutex>
#include <cstdint>
#include <future>

#include <list>
using  namespace std;


class A
{
public:
	int mythread(int myint)
	{
		cout << "func " << "  threadid = " << this_thread::get_id() << " start " << endl << endl;
		cout << "myint = " << myint << endl;
		std::chrono::milliseconds dura(3000);
		std::this_thread::sleep_for(dura);
		cout << "func " << "  threadid = " << this_thread::get_id() << " end " << endl << endl;
		return 5;

	}
};


int main()
{
	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
	A a;
	int num = 100;

	std::future<int> ret =  std::async(&A::mythread,&a,num);//传入可调用对象 &a == std::ref(a)都是引用传递 相当于传递了this指针。
	cout << ret.get() << endl; //堵塞获取值

	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;

	return 0;
}



高手使用

在 C++ 中,std::async 函数用于启动一个异步任务,并且可以与 std::launch 策略一起使用来控制任务的执行方式。std::launchstd::launch 枚举的一个值,它指示 std::async 以延迟(deferred)的方式执行给定的可调用对象。

当你使用 std::launch::deferred 时,std::async 将立即返回一个 std::future 对象,但不会立即执行传入的函数或函数对象。相反,函数的执行被延迟,直到你显式地请求结果,通常是通过调用 std::future::get 方法。这允许你在不阻塞当前线程的情况下启动异步操作。

意思就是说在调用get的函数,才开始执行程序,且是在主线程执行的,不调用的话函数不会运行。        

// ConsoleApplication10.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <thread>
#include <mutex>
#include <cstdint>
#include <future>

#include <list>
using  namespace std;


class A
{
public:
	int mythread(int myint)
	{
		cout << "func " << "  threadid = " << this_thread::get_id() << " start " << endl << endl;
		cout << "myint = " << myint << endl;
		std::chrono::milliseconds dura(3000);
		std::this_thread::sleep_for(dura);
		cout << "func " << "  threadid = " << this_thread::get_id() << " end " << endl << endl;
		return 5;

	}
};


int main()
{
	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
	A a;
	int num = 100;

	std::future<int> ret =  std::async(std::launch::async|std::launch::deferred,&A::mythread,&a,num);//传入可调用对象 &a == std::ref(a)都是引用传递 相当于传递了this指针。
	cout << ret.get() << endl; //堵塞获取值

	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;

	return 0;
}



future

在 C++11 及以后的版本中,std::future 是一个模板类,它提供了一种方式来获取异步操作的结果。当你启动一个异步操作时,例如使用 std::async 或者在新线程中执行一个任务,你可以获得一个 std::future 对象,它代表了异步操作的最终结果。

以下是 std::future 的一些关键特性和用法:

  1. 获取结果:使用 get() 方法来获取异步操作的结果。这将阻塞当前线程直到异步操作完成。

  2. 检查状态:你可以使用 valid() 方法来检查 std::future 对象是否包含一个结果。

  3. 等待结果:使用 wait()wait_for() 方法来等待异步操作完成,而不必阻塞当前线程。

  4. 移动语义std::future 对象只能被移动,不能被复制。

  5. 共享状态std::future 对象共享其关联的异步操作的状态。这意味着你可以有多个 std::future 对象指向同一个异步操作。

  6. 异常处理:如果异步操作抛出异常,get() 方法将重新抛出相同的异常。

  7. 重置:一旦 get() 被调用,std::future 对象将被重置,你不能再从同一个 std::future 对象获取结果。

简单入门

// ConsoleApplication10.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <thread>
#include <mutex>
#include <cstdint>
#include <future>

#include <list>
using  namespace std;


class A
{
public:
	int mythread(int myint)
	{
		cout << "func " << "  threadid = " << this_thread::get_id() << " start " << endl << endl;
		cout << "myint = " << myint << endl;
		std::chrono::milliseconds dura(2000);
		std::this_thread::sleep_for(dura);
		cout << "func " << "  threadid = " << this_thread::get_id() << " end " << endl << endl;
		return 5;
	}
};


int main()
{
	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
	A a;
	int num = 100;
	std::future<int> ret = std::async(std::launch::async, &A::mythread, &a, num);//传入可调用对象 &a == std::ref(a)都是引用传递 相当于传递了this指针。
	std::future_status status = ret.wait_for(std::chrono::milliseconds(1000)); //等待一秒钟。
	if (status == std::future_status::timeout)
	{
		cout << "超时了" << endl;
	}
	else if (status == std::future_status::deferred) //延迟,async启动参数是 deferred就会走这个。
	{
		cout << "延时了" << endl;
	}
	else if (status == std::future_status::ready)
	{
		cout << "已经完成了" << endl;
	}
	//cout << ret.get() << endl; //堵塞获取值 get内部使用了move移动语义,使用后就无值了
	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;

	return 0;
}



中级过度

// ConsoleApplication10.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <thread>
#include <mutex>
#include <cstdint>
#include <future>

#include <list>
using  namespace std;

int  mythread()
{
	cout << "=============func========================== " << " threadid = " << this_thread::get_id() << endl << endl;
	return 100;

}

int main()
{
	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
	future<int>fut = std::async(mythread);//我们一般叫他创建一个异步任务。
	//fut.wait_until(std::chrono::seconds(3));


	// 设置一个特定的截止时间点
	auto now = std::chrono::system_clock::now() + std::chrono::seconds(3);
	fut.wait_until(now);
	// 等待直到截止时间点

	cout << fut.get() << endl;
	
	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;

	return 0;
}



shared_future

在 C++ 中,std::shared_future 是一个模板类,它提供了一种机制来访问异步操作的结果,类似于 std::future,不同之处在于允许多个线程等待同一个共享状态。这与仅可移动的 std::future(因此只有一个实例能指代任何特定的异步结果)形成对比。

std::shared_future 可以通过 std::future 对象隐式转换(参见 std::shared_future 的构造函数),或者通过 std::future::share() 显式转换,无论哪种转换,被转换的那个 std::future 对象自身将失效。

使用 std::shared_future

  1. 获取 std::shared_future 对象std::shared_future 对象可以通过 std::future 对象隐式转换获得,也可以通过调用 std::future::share() 方法显式转换获得。

  2. 等待结果:与 std::future 一样,std::shared_future 也提供了 get(), wait(), wait_for(), 和 wait_until() 方法来等待异步操作的结果。

  3. 异常处理:如果异步操作抛出异常,get() 方法将重新抛出该异常。

  4. 共享状态std::shared_futurestd::promise 相关联,用于管理和使用 std::future 对象。std::shared_future 允许多个线程共享同一个 std::promisestd::packaged_task 的最终结果。

简单使用

// ConsoleApplication10.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <thread>
#include <mutex>
#include <cstdint>
#include <future>

#include <list>
using  namespace std;


class A
{
public:
	int mythread(int myint)
	{
		cout << "func " << "  threadid = " << this_thread::get_id() << " start " << endl << endl;
		cout << "myint = " << myint << endl;
		std::chrono::milliseconds dura(2000);
		std::this_thread::sleep_for(dura);
		cout << "func " << "  threadid = " << this_thread::get_id() << " end " << endl << endl;
		return 5;
	}
};


int main()
{
	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
	A a;
	int num = 100;
	std::future<int> ret = std::async(std::launch::async, &A::mythread, &a, num);//传入可调用对象 &a == std::ref(a)都是引用传递 相当于传递了this指针。
	std::future_status status = ret.wait_for(std::chrono::milliseconds(1000)); //等待一秒钟。
	// std::shared_future<int> shared_ret =  ret.share();
	std::shared_future<int> shared_ret (std::move(ret) ) ;

	if (status == std::future_status::timeout)
	{
		cout << "超时了" << endl;
	}
	else if (status == std::future_status::deferred) //延迟,async启动参数是 deferred就会走这个。
	{
		cout << "延时了" << endl;
	}
	else if (status == std::future_status::ready)
	{
		cout << "已经完成了" << endl;
	}
	cout << shared_ret.get() << endl; //可以获取多次值
	cout << shared_ret.get() << endl; //可以获取多次值
	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;

	return 0;
}



package_task

个人感觉,这个类模板在实际工作中没啥用处。

在 C++ 中,std::packaged_task 是一个类模板,它用于包装任何可调用对象(如函数、lambda 表达式、函数对象等),以便它可以被异步调用。它的返回值或抛出的异常被存储在一个共享状态中,可以通过 std::future 对象访问。std::packaged_taskstd::future 结合使用时,可以轻松管理异步操作。

以下是如何使用 std::packaged_task 的基本步骤:

  1. 创建 std::packaged_task 对象:你可以创建一个 std::packaged_task 对象,并将一个可调用对象(如函数或 lambda 表达式)传递给它。

  2. 获取 std::future 对象:通过调用 get_future() 方法,你可以获取一个 std::future 对象,该对象可以用来获取异步操作的结果。

  3. 启动异步任务:你可以通过在新的线程中调用 std::packaged_task 对象来启动异步任务。

  4. 等待结果:使用 std::future 对象的 get() 方法来等待异步操作完成并获取结果。

简单入门

// ConsoleApplication10.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <thread>
#include <mutex>
#include <cstdint>
#include <future>

#include <list>
using  namespace std;



int mythread(int myint)
{
	cout << "func " << "  threadid = " << this_thread::get_id() << " start " << endl << endl;
	cout << "myint = " << myint << endl;
	std::chrono::milliseconds dura(3000);
	std::this_thread::sleep_for(dura);
	cout << "func " << "  threadid = " << this_thread::get_id() << " end " << endl << endl;
	return 5;

}

int main()
{
	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
	//std::packaged_task类模板
	std::packaged_task<int(int)>mypt(mythread); //把函数mytherad通过package_task包装
	std::thread t1(std::ref(mypt),120);

	t1.join(); //这里的join不可以省略
//执行完毕
	future<int>ret = mypt.get_future(); //获取到future对象。
	cout << "fun finish ret = " << ret.get() << endl;

	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;

	return 0;
}



 中级过度

// ConsoleApplication10.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <thread>
#include <mutex>
#include <cstdint>
#include <future>

#include <list>
using  namespace std;




int mythread(int myint)
{
	cout << "func " << "  threadid = " << this_thread::get_id() << " start " << endl << endl;
	cout << "myint = " << myint << endl;
	std::chrono::milliseconds dura(3000);
	std::this_thread::sleep_for(dura);
	cout << "func " << "  threadid = " << this_thread::get_id() << " end " << endl << endl;
	return 5;

}

int main()
{
	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
	//std::packaged_task类模板
	std::packaged_task<int(int)>mypt([](int num) { 
		cout << "func " << "  threadid = " << this_thread::get_id() << " start " << endl << endl;
		cout << "myint = " << num << endl;
		std::chrono::milliseconds dura(3000);
		std::this_thread::sleep_for(dura);
		cout << "func " << "  threadid = " << this_thread::get_id() << " end " << endl << endl;
		return 5;

		}); //把函数mytherad通过package_task包装
	mypt(300); //包装对象可以直接调用。,在主线程执行,未创建线程

	//执行完毕
	future<int>ret = mypt.get_future(); //获取到future对象。
	cout << "fun finish ret = " << ret.get() << endl;
	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;

	return 0;
}



 高手使用

// ConsoleApplication10.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <thread>
#include <mutex>
#include <cstdint>
#include <future>

#include <list>
using  namespace std;


class A
{
public:
	int mythread(int myint)
	{
		cout << "func " << "  threadid = " << this_thread::get_id() << " start " << endl << endl;
		cout << "myint = " << myint << endl;
		std::chrono::milliseconds dura(3000);
		std::this_thread::sleep_for(dura);
		cout << "func " << "  threadid = " << this_thread::get_id() << " end " << endl << endl;
		return 5;

	}
};

int mythread(int myint)
{
	cout << "func " << "  threadid = " << this_thread::get_id() << " start " << endl << endl;
	cout << "myint = " << myint << endl;
	std::chrono::milliseconds dura(3000);
	std::this_thread::sleep_for(dura);
	cout << "func " << "  threadid = " << this_thread::get_id() << " end " << endl << endl;
	return 5;

}

vector<std::packaged_task<int(int)> > mystatsks;

int main()
{
	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
	//std::packaged_task类模板
	std::packaged_task<int(int)>mypt([](int num) { 
		cout << "func " << "  threadid = " << this_thread::get_id() << " start " << endl << endl;
		cout << "myint = " << num << endl;
		std::chrono::milliseconds dura(3000);
		std::this_thread::sleep_for(dura);
		cout << "func " << "  threadid = " << this_thread::get_id() << " end " << endl << endl;
		return 5;

		}); //把函数mytherad通过package_task包装
	cout << "mypt.valid = " << mypt.valid() << endl;
	//mystatsks.push_back( (mypt) ); //默认是调用拷贝构造,此处内部被删除了
	mystatsks.push_back(std::move(mypt)); //移动到容器中。 mypt就无效了
	cout << "mypt.valid = " << mypt.valid() << endl;

	std::packaged_task<int(int)>mypt2;
	auto it = mystatsks.begin(); //获取第一个元素
	mypt2 = std::move(*it); //移动到mypt2
	cout <<"mystsks.size() = "<< mystatsks.size() << endl;
	mystatsks.erase(it);
	mypt2(123);
	std::future<int>ret =mypt2.get_future() ;
	cout << "ret = " << ret.get();

	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;

	return 0;
}



promise

在 C++11 及以后的版本中,std::promise 是一个模板类,它提供了一种方式来存储值或异常,并且可以在稍后某个时刻通过 std::future 对象访问这些值或异常。std::promise 通常与 std::future 一起使用,以实现线程间的数据传递或同步。

以下是 std::promise 的一些关键特点和用法:

  1. 存储值std::promise 可以存储一个值,这个值可以是任何类型,包括自定义类型。

  2. 存储异常:如果 std::promise 存储了一个异常,那么当 std::future 对象尝试获取值时,这个异常会被抛出。

  3. 设置值:一旦 std::promise 被设置(通过 set_valueset_value_at_thread 方法),它就不能再次被设置。如果尝试再次设置,将抛出 std::future_error 异常。

  4. 获取值std::future 对象使用 get 方法从关联的 std::promise 获取值。如果 std::promise 存储了异常,get 将抛出这个异常。

  5. 等待std::future 对象可以使用 waitwait_for 方法等待 std::promise 设置值。

  6. 移动语义std::promise 对象可以被移动,但不能被复制。

简单入门

// ConsoleApplication10.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <thread>
#include <mutex>
#include <cstdint>
#include <future>

#include <list>
using  namespace std;



void mythread(std::promise<int>& pro, int calc)
{
	calc++;
	std::chrono::milliseconds durn(1000);
	std::this_thread::sleep_for(durn);
	int ret = calc;
	pro.set_value(ret); //在promise中复制,promise英文是许诺的意思
	return;
}
int main()
{
	
	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
#if 0
	std::promise<int> prom;
	std::thread t1(mythread, ref(prom), 200);
	t1.join();
	std::future<int> fu = prom.get_future();
	cout << "val = " << fu.get() << endl;
#else
	std::promise<int> prom;
	std::future<void>fu1 =  std::async(mythread,ref(prom),200);
	std::future<int>furet = prom.get_future();
	cout << "val = " << furet.get() << endl;
#endif

	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;

	return 0;
}



高手使用

// ConsoleApplication10.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <thread>
#include <mutex>
#include <cstdint>
#include <future>

#include <list>
using  namespace std;



void mythread(std::promise<int>& pro, int calc)
{
	cout << "mythread start " << endl;
	calc++;
	std::chrono::milliseconds durn(1000);
	std::this_thread::sleep_for(durn);
	int ret = calc;
	pro.set_value(ret); //在promise中复制,promise英文是许诺的意思
	return;
}

void mythread2(std::future<int>& fut)
{
	cout << "mythread2 start " << endl;
	int num = fut.get();
	cout << "mythread2 = " << num << endl;
}

int main()
{
	
	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
#if 1
	std::promise<int> prom;
	std::thread t1(mythread, ref(prom), 200);
	//t1.join();
	t1.detach();

	std::future<int> fu = prom.get_future();
	//cout << "val = " << fu.get() << endl; //拿不到值就死等,直到拿到值

	std::thread t2(mythread2, std::ref(fu));
	t2.join();//等线程2执行完毕。


#else
	std::promise<int> prom;
	std::future<void>fu1 =  std::async(mythread,ref(prom),200);
	std::future<int>furet = prom.get_future();
	cout << "val = " << furet.get() << endl;  //拿不到值就死等,直到拿到值
#endif

	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;

	return 0;
}



atomic原子操作

在 C++ 中,原子操作(Atomic Operations)是用于在多线程环境中安全地执行单个变量操作的方法,而不需要使用互斥锁(mutexes)。原子操作可以保证在多线程同时访问和修改共享数据时,数据的完整性和一致性不会受到破坏。

C++11 引入了 <atomic> 头文件,它提供了一组原子类型和函数,用于执行原子操作。原子类型包括 std::atomic_flagstd::atomic_boolstd::atomic_char 等,以及针对各种整数类型的原子类型,如 std::atomic<int>std::atomic<unsigned long long> 等。

以下是一些常见的原子操作和它们的作用:

  1. 初始化std::atomic_flag 和其他原子类型可以使用构造函数初始化。

  2. 存储和加载store 方法用于将一个值原子地存储到原子对象中,load 方法用于安全地加载原子对象的值。

  3. 原子交换exchange 方法用于原子地交换原子对象中的值。

  4. 比较并交换compare_exchange_weakcompare_exchange_strong 方法用于原子地比较并交换原子对象中的值,如果当前值与预期值相等,则替换为新值。

  5. 增加和减少fetch_addfetch_subfetch_andfetch_orfetch_xor 等方法用于原子地对原子对象执行增加、减少、逻辑与、逻辑或、逻辑异或等操作。

  6. 等待通知waitnotify_one/notify_all 方法用于线程同步,允许线程在某个条件成立之前挂起,并在条件成立时唤醒。

原子操作通常比使用互斥锁有更低的开销,因为它们不需要操作系统介入来管理锁。然而,它们不提供互斥锁那样的复杂同步机制,比如锁的可重入性。

简单使用

// ConsoleApplication10.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <thread>
#include <mutex>
#include <cstdint>
#include <future>

#include <list>
using  namespace std;

//int g_c = 0; //普通使用会出现线程竞争问题。
// std::atomic<int> g_c = 0; //使用atomic不会不出现问题。
//	std::atomic<int>at2(g_c.load()); //可以加载原子操作
//	g_c.store(12); //存储值



void  mythread()
{
	for (int i = 0; i < 100000; i++)
	{
		//g_c++; //普通用法
        //g_c = g_c + 1; //这样写结果会计算错误。
		g_c.fetch_add(1, std::memory_order_relaxed); //使用fetch_add取出数据并添加数据

		//请注意,std::memory_order_relaxed 是一个内存顺序选项,它确保操作的顺序性。
		// C++ + 提供了一系列内存顺序选项,允许开发者根据具体需求选择合适的内存顺序保证。
			//memory_order_relaxed, //放宽
			//memory_order_consume, //消费
			// 
			//memory_order_acquire, //获得
			//memory_order_release, //释放
			//memory_order_acq_rel,
			//memory_order_seq_cst



	}
}

int main()
{
	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
	thread th1(mythread);
	thread th2(mythread);
	th1.join();
	th2.join();
	cout << "g_c = " << g_c << endl;


	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;

	return 0;
}



其他线程使用可以参数我主页中的 C语言:高级并发操作(线程)文章。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

only-lucky

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值