进程与线程:从概念到C#实操

在计算机编程领域,进程和线程是操作系统调度资源、执行任务的核心概念。其中最关键的关系是:一个进程内可以包含多个线程,线程内部绝不可能有进程;一个程序可通过多线程并行完成不同或相同的工作,提升运行效率。本文将先拆解进程与线程的核心区别,再以C#编程语言为例,通过可运行的代码示例,让抽象概念落地。

一、核心定义:进程与线程的“大团队”与“小成员”

操作系统就像一个高效的“公司管理者”,进程和线程则是这个公司里的“工作团队”和“团队成员”:

  • 进程:是操作系统分配资源(内存、CPU时间片、文件句柄)的最小独立单位。可以理解为“独立运行的程序实例”——比如你编译运行的C#控制台程序、打开的微信、记事本,每一个都是独立进程。每个进程有专属内存空间,不同进程默认互不干扰,如同公司里独立的部门,各有各的办公室和资源。
  • 线程:是操作系统调度CPU执行任务的最小单位,也叫“轻量级进程”。线程必须依附于进程存在,一个进程可包含一个或多个线程,这些线程共享进程的内存、资源(如打开的文件、网络连接),如同部门里的员工,共享办公室资源,协同完成部门任务。

在C#语境下,一个.exe程序的运行实例就是一个进程,程序默认的Main方法运行在“主线程”中;我们可通过System.Threading命名空间创建多个子线程,通过System.Diagnostics命名空间操作进程。

二、核心区别:进程与线程的“从属关系”

  1. 进程是“容器”,线程是“容器内的执行者”
    进程为线程提供运行的基础环境,线程是真正执行具体任务的单元。比如C#编写的微信客户端进程,可包含“接收消息线程”“UI渲染线程”“音频播放线程”,这些线程都属于微信进程;但反过来,“接收消息线程”里不可能“装下”一个新的QQ进程——线程没有容纳进程的能力。

  2. 独立性与共享性差异

    • 进程间完全独立:一个进程崩溃通常不影响其他进程(比如关闭记事本进程,不会导致微信进程崩溃);
    • 线程共享进程资源:同一进程内的线程共享内存、资源,一个线程出错可能导致整个进程崩溃(比如C#程序中某个线程未处理的异常,可能让整个.exe程序闪退)。

三、C#实操:用代码验证核心区别

示例1:单进程多线程完成不同工作(模拟微信逻辑)

这个例子中,我们创建一个C#控制台进程,在进程内启动3个线程,分别模拟“接收消息”“显示界面”“播放语音”这3个不同工作,体现“一个进程包含多个线程”且“线程共享进程资源”的特性。

using System;
using System.Threading;

namespace ProcessAndThreadDemo
{
    class Program
    {
        // 全局变量(进程内所有线程共享)
        private static string _receivedMessage = "";

        static void Main(string[] args)
        {
            // 获取当前进程ID,整个程序只有一个进程ID
            int currentProcessId = System.Diagnostics.Process.GetCurrentProcess().Id;
            Console.WriteLine($"微信模拟进程(ID:{currentProcessId})启动,主线程开始运行");

            // 1. 创建“接收消息线程”:处理网络消息接收
            Thread receiveMsgThread = new Thread(ReceiveMessage);
            receiveMsgThread.Name = "接收消息线程";
            receiveMsgThread.IsBackground = true; // 后台线程,进程退出则终止

            // 2. 创建“显示界面线程”:渲染聊天界面
            Thread showUIThread = new Thread(ShowUI);
            showUIThread.Name = "显示界面线程";
            showUIThread.IsBackground = true;

            // 3. 创建“播放语音线程”:处理语音提示
            Thread playVoiceThread = new Thread(PlayVoice);
            playVoiceThread.Name = "播放语音线程";
            playVoiceThread.IsBackground = true;

            // 启动所有线程(均属于当前进程)
            receiveMsgThread.Start();
            showUIThread.Start();
            playVoiceThread.Start();

            // 主线程等待用户输入,避免进程直接退出
            Console.WriteLine("按任意键退出微信模拟程序...");
            Console.ReadKey();
            Console.WriteLine("微信进程终止,所有线程将被销毁");
        }

        /// <summary>
        /// 接收消息线程的执行逻辑
        /// </summary>
        static void ReceiveMessage()
        {
            int count = 0;
            while (true)
            {
                count++;
                _receivedMessage = $"好友发送的消息{count}"; // 共享全局变量
                Console.WriteLine($"[{Thread.CurrentThread.Name}] 接收到消息:{_receivedMessage}");
                Thread.Sleep(2000); // 模拟每2秒接收一条消息
            }
        }

        /// <summary>
        /// 显示界面线程的执行逻辑
        /// </summary>
        static void ShowUI()
        {
            while (true)
            {
                if (!string.IsNullOrEmpty(_receivedMessage))
                {
                    Console.WriteLine($"[{Thread.CurrentThread.Name}] 聊天界面显示:{_receivedMessage}");
                }
                Thread.Sleep(1000); // 模拟每秒刷新一次界面
            }
        }

        /// <summary>
        /// 播放语音线程的执行逻辑
        /// </summary>
        static void PlayVoice()
        {
            while (true)
            {
                Console.WriteLine($"[{Thread.CurrentThread.Name}] 播放语音提示:有新消息啦!");
                Thread.Sleep(3000); // 模拟每3秒播放一次语音
            }
        }
    }
}
代码关键解析:
  • 进程唯一性:Process.GetCurrentProcess().Id获取的进程ID贯穿整个程序,所有线程都属于这个进程;
  • 资源共享:全局变量_receivedMessage被3个线程共享——“接收消息线程”修改它,“显示界面线程”读取它,体现同一进程内线程的资源共享特性;
  • 多线程并行:运行程序后,3个线程会同时工作,你会看到“接收消息”“显示界面”“播放语音”的日志交替输出,模拟微信同时处理多个任务的场景。
运行效果片段:

image

示例2:单进程多线程完成相同工作(模拟多线程下载)

这个例子模拟“多线程下载文件”:一个C#进程内启动5个线程,共同下载同一个文件的10个片段(将相同工作拆分),提升整体效率。

using System;
using System.Threading;

namespace MultiThreadDownloadDemo
{
    class Program
    {
        // 模拟文件总片段数
        private const int TotalFileParts = 10;
        // 已下载片段数(进程内线程共享,需保证线程安全)
        private static int _downloadedParts = 0;

        static void Main(string[] args)
        {
            int currentProcessId = System.Diagnostics.Process.GetCurrentProcess().Id;
            Console.WriteLine($"下载工具进程(ID:{currentProcessId})启动");

            // 创建5个线程,共同下载10个文件片段
            for (int i = 1; i <= 5; i++)
            {
                Thread downloadThread = new Thread(DownloadFilePart);
                downloadThread.Name = $"下载线程{i}";
                downloadThread.IsBackground = true;
                downloadThread.Start(i); // 传入线程编号,分配下载片段
            }

            // 主线程等待所有片段下载完成
            while (_downloadedParts < TotalFileParts)
            {
                Thread.Sleep(500);
                Console.WriteLine($"进度:已下载 {_downloadedParts}/{TotalFileParts} 片段");
            }

            Console.WriteLine("文件下载完成!");
            Console.ReadKey();
        }

        /// <summary>
        /// 下载单个文件片段的逻辑
        /// </summary>
        /// <param name="threadNum">线程编号</param>
        static void DownloadFilePart(object threadNum)
        {
            int num = (int)threadNum;
            // 每个线程负责2个片段(10个片段/5个线程)
            for (int i = (num - 1) * 2 + 1; i <= num * 2; i++)
            {
                Console.WriteLine($"[{Thread.CurrentThread.Name}] 正在下载片段{i}...");
                Thread.Sleep(800); // 模拟下载耗时
                // 原子操作:避免多个线程同时修改计数导致错误
                Interlocked.Increment(ref _downloadedParts);
                Console.WriteLine($"[{Thread.CurrentThread.Name}] 片段{i}下载完成!");
            }
        }
    }
}

image

代码关键解析:
  • 相同工作拆分:5个线程共同完成“下载文件”这一相同任务,每个线程负责2个片段,相比单线程下载,能充分利用CPU和网络资源;
  • 线程安全:使用Interlocked.Increment保证对共享变量_downloadedParts的修改是原子操作,避免多个线程同时修改导致计数错误;
  • 进程边界:所有下载线程都属于同一个进程,无需跨进程通信,共享计数变量即可同步进度。

示例3:验证“线程无法包含进程”

这个例子展示:即使在C#的某个线程中调用“创建新进程”的代码,新进程也是独立的,并非“属于这个线程”——线程只是“触发创建进程的执行者”,而非“包含进程的容器”。

using System;
using System.Diagnostics;
using System.Threading;

namespace ProcessCreateDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            int mainProcessId = Process.GetCurrentProcess().Id;
            Console.WriteLine($"主程序进程(ID:{mainProcessId})启动,主线程运行中");

            // 创建一个子线程,让它去启动新进程
            Thread createProcessThread = new Thread(CreateNewProcess);
            createProcessThread.Name = "创建进程的线程";
            createProcessThread.IsBackground = true;
            createProcessThread.Start();

            Console.ReadKey();
        }

        /// <summary>
        /// 在子线程中启动新进程(记事本)
        /// </summary>
        static void CreateNewProcess()
        {
            Console.WriteLine($"[{Thread.CurrentThread.Name}] 线程ID:{Thread.CurrentThread.ManagedThreadId},准备启动记事本进程");

            // 启动记事本(独立的新进程)
            ProcessStartInfo psi = new ProcessStartInfo
            {
                FileName = "notepad.exe",
                WindowStyle = ProcessWindowStyle.Normal
            };

            Process notepadProcess = Process.Start(psi);
            Console.WriteLine($"[{Thread.CurrentThread.Name}] 启动的记事本进程ID:{notepadProcess.Id}");

            // 等待记事本进程关闭
            notepadProcess.WaitForExit();
            Console.WriteLine($"[{Thread.CurrentThread.Name}] 记事本进程已关闭");
        }
    }
}

image

代码关键解析:
  • 进程独立性:虽然“创建进程的线程”执行了启动记事本的代码,但记事本是一个独立的新进程(有自己的进程ID),和主程序进程平级;
  • 核心验证:你可在任务管理器中看到两个进程(主程序.exe + notepad.exe),关闭主程序进程,记事本进程依然能正常运行——这证明线程无法包含进程,只是触发了进程的创建。

四、C#开发中使用进程/线程的核心价值

  1. 提升效率:单进程单线程只能“串行做事”,多线程可“并行处理”,充分利用CPU多核资源(比如多线程下载、多线程处理数据);
  2. 节省资源:创建线程的开销远小于创建进程(线程共享进程资源,无需重新分配内存);
  3. 通信简单:同一进程内的线程通信(如共享变量、lock锁、信号量)比跨进程通信(如管道、套接字)更简单。

总结

  1. 核心从属关系:进程是资源分配的独立单元,可包含多个线程;线程是CPU调度的最小单元,必须依附进程存在,线程内无法包含进程(C#中即使线程触发进程创建,新进程也与原进程平级);
  2. C#实操要点:通过System.Threading.Thread创建线程,通过System.Diagnostics.Process操作进程;同一进程内的线程共享内存资源,可完成不同/相同工作;
  3. 应用价值:C#程序通过单进程多线程,能并行处理任务、提升运行效率,是开发高性能程序的核心手段之一。

👋 关注我!持续分享 C# 实战技巧、代码示例 & 技术干货

  • 获取示例代码,轻松上手!
C# —— 进程与线程的理解 定义 进程:进程是资源(CPU、内存等)分配的基本单位,具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。 线程:线程是进程的一个实体,是独立运行和独立调度的基本单位(CPU上真正运行的是线程)。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。 区别 进程拥有自己的资源空间,每启动一个进程,系统就会为它分配地址空间;而线程与CPU资源分配无关,多个线程 阅读详情

相关推荐

C#进程,线程基础知识

介绍C#进程与线程的基础知识

ForstT14的博客 1049

C#进程与线程

在操作系统中,每运行一个程序都会开启一个进程,一个进程由多个线程构成。 线程是程序执行流中最小的单元。 在应用程序中分为单线程程序和多线程程序。 • 单线程程序是指在一个进程空间中只有一个线程在执行; • 多线程程序是指在一个进程空间中有多个线程在执行,并共享同一个进程的大小。 进程指在每个操作系统中自动启动的系统进程和一些自动启动的应用程序进程,在 Windows 操作系统中提供了任务管理器来查看当前启动的进程,并能关闭指定的进程。 文章目录C# Process:进程类Process 简介操作进程C# T

weixin_42715287的博客 1342

C#进程和线程

进程和线程 进程(Process) 进程是系统中的一个基本概念。 一个程序是一个进程,一个进程有多个线程。 Process:提供对本地和远程进程的访问并能够启动和停止本地系统进程。 前台线程 只有所有的前台线程都关闭了,才能完成程序关闭 后台线程 只要所有的前台线程关闭了,后台线程会自动结束 线程(Thread):是进程中的基本执行单元,是操作系统分配CPU时间的基本单位 ,在进程入口执行的第一个...

皮卡丘丘丘~~~ 584

进程和线程概念

进程是一个独立运行的应用程序实例,它包含了应用程序代码和所有需要的资源,如内存、文件描述符、环境变量等。每个进程在操作系统中是相对独立的个体,并拥有自己的地址空间(内存),不会与其他进程共享。线程是进程中的一个执行单元,也称为轻量级进程。一个进程可以包含一个或多个线程,这些线程可以共享进程的地址空间和资源(如内存、文件描述符),但它们具有自己的栈、寄存器等信息。进程适用于需要隔离资源的场景,具有较高的安全性和稳定性。线程适合在同一个程序内并发处理多个任务,有助于提升程序的性能和资源利用率。

猪猪侠 1176

【从零开始入门unity游戏开发之——C#篇37】进程、线程、C# 中实现多线程有多种方案和async/await异步编程

进程、线程、C# 中实现多线程有多种方案和async/await异步编程

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 1467

C#每日面试题-进程和线程的区别

在C#面试的基础高频题中,“进程和线程的区别”绝对占有一席之地。很多新手会把两者理解成“大程序和小程序”的关系,实则不然——它们是操作系统资源管理和任务调度的两个核心层级,核心定位、资源占用、调度方式完全不同。今天我们用“工厂比喻+代码实例+底层拆解”的方式,把这个知识点讲透,既能让新手快速理解,也能满足面试所需的深度拓展。

Amon的专栏 1607

【C#】C#中进程和线程的区别

进程是操作系统分配资源的基本单位,适用于需要隔离的应用程序 线程是程序执行的基本单位,适用于需要高效利用CPU,执行并发任务的程序 线程池:通过重用现有线程来优化线程的创建和销毁功能,从而提高应用程序的性能和资源利用率

冰茶的博客 1161

【详细图文】使用MobaXterm的SSH功能远程连接服务器,并创建pytorch环境,使用yolov8训练数据集

使用MobaXterm的SSH功能远程连接服务器,并创建pytorch环境,使用yolov8训练数据集,以及MobaXterm使用介绍和linux基础命令介绍。

qq_62844901的博客 3382

C#上位机--进程和线程的区别

进程和线程掌握好,程序流程烦恼少

zuowusheng的博客 3578

ICLR 2022)ODConv:即插即用的动态卷积 (附代码)

一定程度上讲,ODConv可以视作CondConv的延续,将CondConv中一个维度上的动态特性进行了扩展,同时了考虑了空域、输入通道、输出通道等维度上的动态性,故称之为全维度动态卷积。ODConv是一种动态卷积算法,它的原理是在卷积过程中,根据输入数据的特征动态地调整卷积核的形状和大小,以适应不同的输入数据。具体来说,ODConv通过引入一个可学习的形变模块,根据输入数据的特征动态地调整卷积核的形状和大小,从而提高了卷积神经网络的性能。此外,相比常规卷积,动态卷积的卷积核参数往往是其n倍。

PLANTTHESON的博客 2万+

【IEEE】从Accept到Online:详解Proof校样阶段的关键操作与避坑指南

本文详细解析了IEEE论文从Accept到Online的Proof校样阶段关键操作流程与避坑指南。重点介绍了IEEE Author Gateway平台的使用技巧、校样稿首页的风险点检查,以及多人协作校对策略,帮助作者高效完成论文出版前的最后审核,确保学术成果准确呈现。

weixin_30591551的博客 357

C# 进程与线程 操作

using System.Diagnostics;//进程的命名空间               //利用进程可以打开一些应用程序             Process.Start("calc"); //计算器             Process.Start("mspaint"); // 画图工具             Process.Start("notepad");// 记事

有塔耶奥多的专栏 3228

使用Simulink进行基于IIR滤波器的设计与稳定性分析仿真

IIR滤波器的稳定性与其系统函数的极点位置密切相关。如果所有极点都位于Z平面的单位圆内,则该滤波器是稳定的;否则,滤波器将是不稳定的。在设计IIR滤波器时,确保其稳定性对于避免输出信号发散至关重要。明确你的系统需求,比如输入信号类型(如正弦波)、采样率、IIR滤波器的设计参数(如滤波器类型、阶数、截止频率等)。假设我们需要设计一个低通IIR滤波器,用来去除高于500Hz的所有频率成分,保留低于500Hz的信号成分。采样率为8kHz。

xiaoheshang_123的博客 150

C# 多线程入门(进程,线程,线程池基本概念及简单使用)

一. 基本概念 1. 进程 2. 线程 二. 多线程 1. 基本理解 2. 多线程的好处 3. 使用多线程需要注意 三. Thread 1. 基本弃用 四. ThreadPool 1. 对线程池的理解 2. 使用线程池好处 3. 使用线程池的注意事项 4. 线程池的简单使用 五. Parallel 六. Task 1. 简介 2. Task的简单使用

qq_33101689的博客 2357

基于若依的ruoyi-nbcio-plus支持VForm3表单字段数据保存到数据库的一种方法——全网首创(一)

基于若依的ruoyi-nbcio-plus支持VForm3表单字段数据保存到数据库的一种方法——全网首创(一)

宁波阿成的博客 658

Unity 工具 之 ZXing QRCode 二维码的生成和识别(可设置生成不同颜色的/带图标的二维码)

Unity 基础 之 ZXing QRCode 二维码的生成和识别(可设置生成不同颜色的/带图标的二维码) 目录 Unity 基础 之 ZXing QRCode 二维码的生成和识别(可设置生成不同颜色的/带图标的二维码) 一、简单介绍 二、实现原理 三、注意实现 四、效果预览 五、ZXing.net ( zxing.unity.dll ) 插件下载 六、实现步骤 七、关键代码 一、简单介绍 Unity 开发中,自己整理的一些游戏开发可能用到的功能,整理归档,方便游...

仙魁XAN 9824

C#:进程/线程/多线程/AppDomain详解

本文介绍了C#中对于进程/线程/多线程/AppDomain及同步异步的理解 本文可以结合之前的【C#-有关同步异步(Async/Await)的理解】这篇文章理解

龟速船长 1806

多摄像头场景下 ISP 的同步与调度机制实战解析

在智能手机多摄系统中,主摄、广角、长焦、微距等摄像头的协同工作能力直接决定了影像系统的体验上限。而支撑这一能力的核心之一,正是 ISP 的同步与调度机制。ISP 需要处理多个 Sensor 的输入流、统一时序、资源仲裁、并发调度,并支持在不同模式间快速切换(如预览 → 拍照 → 视频)。本文将基于 Qualcomm、MTK、HiSilicon 平台的实际架构,系统性剖析 ISP 在多摄场景下的时序控制、Pipeline 配置、Buffer 管理与故障恢复机制,并分享实战中的典型问题与调试方案。

努力分享一些人工智能、计算机视觉、影像等相关的知识干货! 2347

C#多线程编程(一)进程与线程

一、 进程 简单来说,进程是对资源的抽象,是资源的容器,在传统操作系统中,进程是资源分配的基本单位,而且是执行的基本单位,进程支持并发执行,因为每个进程有独立的数据,独立的堆栈空间。一个程序想要并发执行,开多个进程即可。 Q1:在单核下,进程之间如何同时执行? 首先要区分两个概念——并发和并行 并发:并发是指在一段微小的时间段中,有多个程序代码段被CPU执行,宏观上表现出来就是多个程序能”同时“执行。 并行:并行是指在一个时间点,有多个程序段代码被CPU执行,它才是...

深蓝旭的博客 984

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

C#学习教程14——进程与线程

C#学习教程,适合新手小白,附带学习源码。

Kint的博客 2331

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

C#理解进程、线程和任务

关于 C# 中进程、线程和任务的概述,以及有关评估类型效率和监控性能和资源使用情况的详细内容

qq_42497776的博客 1296

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

C# 进程与线程

1. 进程 进程是Windows 系统中的一个基本概念,它包含着一个运行程序的所需要的资源。一个正在运行的应用程序在操作系统中被视为一个进程,进程可以包括一个或多个线程。 2. 线程 线程是进程中的基本执行单位,是操作系统分配cpu时间的基本单位。在进程入口第一个线程被视为进程的主线程。线程主要是由CPU寄存器,调用栈和线程本地存储器(Thread Local Storage, TLS)组成的。 CPU寄存器:记录当前执行线程的状态。 调用栈:主要用于维护线程所调用到的内存与数据。 线程本地存储器:主要用

weixin_43537631的博客 1099

C# 第八章『多线程』◆第1节:进程与线程

暂停状态与就绪状态是不一样的:暂停状态下的线程是持有系统资源的,只是没有进行任何操作。多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。线程是程序中一个执行流,每个线程都有自己的专有寄存器(栈、指针、程序计数器等),但代码区是共享的,即不同的进程可以执行同样的函数。注意:一个进程可以有多个线程,一个线程必须有一个父进程,一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。

Halcon机器视觉 1206

8. C#多线程基础概念

什么是进程(Process)一个正在运行的程序实例就是一个进程,拥有独立的内存空间和资源.每个进程都在自己的内存空间内运行,相互之间不直接共享内存,进程间通信一般需要一些机制,比如进程间通信IPC.每一个进程都有自己的一个主线程,而这个主线程是程序的入口点,它可以创建其他线程来执行不同的任务什么是线程(Thread)线程是进程内的一个执行单元,也是操作系统可执行的最小单元.一个进程中的多个线程共享进程的资源,它们之间可以共享数据.线程在程序中是可以并行执行的.多进程。

Fioman_GYM的博客 1087
上一篇: WPF 数据模板(DataTemplate):优雅实现数据与 UI 的解耦
下一篇: WPF数据绑定入门:从传统事件到5种绑定模式
bugcome_com
博客等级 码龄3年 32粉丝 131原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

打赏作者

bugcome_com

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

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

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

打赏作者

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

抵扣说明:

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

余额充值