精华内容
下载资源
问答
  • 被问懵了:一进程最多可以创建多少个线程

    千次阅读 多人点赞 2021-07-15 09:19:09
    其实他看这面经很问题,没有说明是什么操作系统,以及是多少位操作系统。 因为不同的操作系统和不同位数的操作系统,虚拟内存可能是不一样。 Windows 系统我不了解,我就说说 Linux 系统。 在 Linux 操作系统...

    大家好,我是小林。

    昨天有位读者问了我这么个问题:


    大致意思就是,他看了一个面经,说虚拟内存是 2G 大小,然后他看了我的图解系统 PDF 里说虚拟内存是 4G,然后他就懵逼了。

    其实他看这个面经很有问题,没有说明是什么操作系统,以及是多少位操作系统。

    因为不同的操作系统和不同位数的操作系统,虚拟内存可能是不一样多。

    Windows 系统我不了解,我就说说 Linux 系统。

    在 Linux 操作系统中,虚拟地址空间的内部又被分为内核空间和用户空间两部分,不同位数的系统,地址 空间的范围也不同。比如最常⻅的 32 位和 64 位系统,如下所示:

    通过这里可以看出:

    • 32 位系统的内核空间占用 1G ,位于最高处,剩下的 3G 是用户空间;
    • 64 位系统的内核空间和用户空间都是 128T ,分别占据整个内存空间的最高和最低处,剩下的中
      间部分是未定义的。

    接着,来看看读者那个面经题目:一个进程最多可以创建多少个线程?

    这个问题跟两个东西有关系:

    • 进程的虚拟内存空间上限,因为创建一个线程,操作系统需要为其分配一个栈空间,如果线程数量越多,所需的栈空间就要越大,那么虚拟内存就会占用的越多。
    • 系统参数限制,虽然 Linux 并没有内核参数来控制单个进程创建的最大线程个数,但是有系统级别的参数来控制整个系统的最大线程个数。

    我们先看看,在进程里创建一个线程需要消耗多少虚拟内存大小?

    我们可以执行 ulimit -a 这条命令,查看进程创建线程时默认分配的栈空间大小,比如我这台服务器默认分配给线程的栈空间大小为 8M。

    在前面我们知道,在 32 位 Linux 系统里,一个进程的虚拟空间是 4G,内核分走了1G,留给用户用的只有 3G

    那么假设创建一个线程需要占用 10M 虚拟内存,总共有 3G 虚拟内存可以使用。于是我们可以算出,最多可以创建差不多 300 个(3G/10M)左右的线程。

    如果你想自己做个实验,你可以找台 32 位的 Linux 系统运行下面这个代码:

    由于我手上没有 32 位的系统,我这里贴一个网上别人做的测试结果:

    如果想使得进程创建上千个线程,那么我们可以调整创建线程时分配的栈空间大小,比如调整为 512k:

    $ ulimit -s 512
    

    说完 32 位系统的情况,我们来看看 64 位系统里,一个进程能创建多少线程呢?

    我的测试服务器的配置:

    • 64 位系统;
    • 2G 物理内存;
    • 单核 CPU。

    64 位系统意味着用户空间的虚拟内存最大值是 128T,这个数值是很大的,如果按创建一个线程需占用 10M 栈空间的情况来算,那么理论上可以创建 128T/10M 个线程,也就是 1000多万个线程,有点魔幻!

    所以按 64 位系统的虚拟内存大小,理论上可以创建无数个线程。

    事实上,肯定创建不了那么多线程,除了虚拟内存的限制,还有系统的限制。

    比如下面这三个内核参数的大小,都会影响创建线程的上限:

    • /proc/sys/kernel/threads-max,表示系统支持的最大线程数,默认值是 14553
    • /proc/sys/kernel/pid_max,表示系统全局的 PID 号数值的限制,每一个进程或线程都有 ID,ID 的值超过这个数,进程或线程就会创建失败,默认值是 32768
    • /proc/sys/vm/max_map_count,表示限制一个进程可以拥有的VMA(虚拟内存区域)的数量,具体什么意思我也没搞清楚,反正如果它的值很小,也会导致创建线程失败,默认值是 65530

    那接下针对我的测试服务器的配置,看下一个进程最多能创建多少个线程呢?

    我在这台服务器跑了前面的程序,其结果如下:

    可以看到,创建了 14374 个线程后,就无法在创建了,而且报错是因为资源的限制。

    前面我提到的 threads-max 内核参数,它是限制系统里最大线程数,默认值是 14553。

    我们可以运行那个测试线程数的程序后,看下当前系统的线程数是多少,可以通过 top -H 查看。

    左上角的 Threads 的数量显示是 14553,与 threads-max 内核参数的值相同,所以我们可以认为是因为这个参数导致无法继续创建线程。

    那么,我们可以把 threads-max 参数设置成 99999:

    echo 99999 > /proc/sys/kernel/threads-max
    

    设置完 threads-max 参数后,我们重新跑测试线程数的程序,运行后结果如下图:

    可以看到,当进程创建了 32326 个线程后,就无法继续创建里,且报错是无法继续申请内存。

    此时的上限个数很接近 pid_max 内核参数的默认值(32768),那么我们可以尝试将这个参数设置为 99999:

    echo 99999 > /proc/sys/kernel/pid_max
    

    设置完 pid_max 参数后,继续跑测试线程数的程序,运行后结果创建线程的个数还是一样卡在了 32768 了。

    当时我也挺疑惑的,明明 pid_max 已经调整大后,为什么线程个数还是上不去呢?

    后面经过查阅资料发现,max_map_count 这个内核参数也是需要调大的,但是它的数值与最大线程数之间有什么关系,我也不太明白,只是知道它的值是会限制创建线程个数的上限。

    然后,我把 max_map_count 内核参数也设置成后 99999:

    echo 99999 > /proc/sys/kernel/max_map_count 
    

    继续跑测试线程数的程序,结果如下图:

    当创建差不多 5 万个线程后,我的服务器就卡住不动了,CPU 都已经被占满了,毕竟这个是单核 CPU,所以现在是 CPU 的瓶颈了。

    我只有这台服务器,如果你们有性能更强的服务器来测试的话,有兴趣的小伙伴可以去测试下。

    接下来,我们换个思路测试下,把创建线程时分配的栈空间调大,比如调大为 100M,在大就会创建线程失败。

    ulimit -s 1024000
    

    设置完后,跑测试线程的程序,其结果如下:

    总共创建了 26390 个线程,然后就无法继续创建了,而且该进程的虚拟内存空间已经高达 25T,要知道这台服务器的物理内存才 2G。

    为什么物理内存只有 2G,进程的虚拟内存却可以使用 25T 呢?

    因为虚拟内存并不是全部都映射到物理内存的,程序是有局部性的特性,也就是某一个时间只会执行部分代码,所以只需要映射这部分程序就好。

    你可以从上面那个 top 的截图看到,虽然进程虚拟空间很大,但是物理内存(RES)只有使用了 400 多M。

    好了,简单总结下:

    • 32 位系统,用户态的虚拟空间只有 3G,如果创建线程时分配的栈空间是 10M,那么一个进程最多只能创建 300 个左右的线程。
    • 64 位系统,用户态的虚拟空间大到有 128T,理论上不会受虚拟内存大小的限制,而会受系统的参数或性能限制。

    絮叨絮叨

    小林在 CSDN 写了很多图解网络和操作系统的系列文章,很高兴收获到很朋友的认可和支持,正好最近图解网络和操作系统的文章连载的有 20+ 篇了,也算有个体系了。

    在这里插入图片描述

    所以为了方便大家阅读,小林把自己原创的图解网络和图解操作系统整理成了 PDF,一整理后,没想到每个图解都输出了 15 万字 + 500 张图,质量也是杠杠的,有很多朋友特地私信我,看了我的图解拿到了大厂的offer。

    图解系统 PDF 开源下载:图解系统 PDF 下载地址(点击)

    图解网络 PDF 开源下载:图解网络 PDF 下载地址(点击)


    我是小林,今天的你,比昨天更博学了吗?

    展开全文
  • 32位的linux操作系统,一进程4G虚拟内存,内核会分走1G,一般的linux系统创建一个线程会分配一栈空间,一般栈空间大小在8-10M。所以可以创建大约300个线程

    一个32位的linux操作系统,一个进程有4G虚拟内存,内核会分走1G,一般的linux系统创建一个线程会分配一个栈空间,一般栈空间大小在8-10M。所以可以创建大约300个线程。

    展开全文
  • 先写在前面:thread模块,简单来说,cpu的反复横跳而已:要写并发机制,还得用多进程multiprocessing模块,想要快速实现的朋友...多线程类似于同时执行多不同程序,多线程运行如下优点:使用线程可以把占据长...

    先写在前面:

    thread模块,简单来说,cpu的反复横跳而已:

    要写并发机制,还得用多进程multiprocessing模块,想要快速实现的朋友可以看看我这篇文章:是枝裕和:内:python中的多进程​zhuanlan.zhihu.com1be9555fb3f6217dd145a418566b8dfc.png

    当然threads也是有用的(轻量,简易,操作简单)

    大家可以看我下面举例子

    多线程是什么?

    多线程类似于同时执行多个不同程序,多线程运行有如下优点:

    使用线程可以把占据长时间的程序中的任务放到后台去处理。

    用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度。

    程序的运行速度可能加快。

    在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。

    我拿两个视频做简单的对比:8c6f45edb23932e1aec0883fbbc6ff63.png开多线程https://www.zhihu.com/video/1109427449007489024f716c97f31db05f40a8ad8e387f9b9a7.png循环处理文件https://www.zhihu.com/video/1109427570772303872

    那学习多线程对数据分析有什么作用呢,我们可以看看:

    现在我有0,1,2三个csv文件,如果我要获取数据并分析,例如用for循环,那只能按顺序读取,这样就会造成效率低下:

    代码如下:

    import operator

    import csv

    import os

    def read_file(filpos,i):

    with open(filpos+str(i)+".csv") as f:

    reader=csv.reader(f)

    for i in reader:

    print(i)

    for i in range(0,3):

    read=read_file("D:/zhihu/",i)

    当然你觉得没什么问题,但是如果有20个文件,每个文件有20000条数据,这种方法就会显得非常愚笨,所以我们可以采用多线程的方式同时间读取0,1,2csv文件。

    第一步:import threading 模块

    import threading

    第二步:改下一下代码:

    既可以同时打开并运行多个文件

    import operator

    import csv

    import time

    import threading

    from time import ctime

    def read_file(filpos,i):

    with open(filpos+str(i)+".csv") as f:

    reader=csv.reader(f)

    for i in reader:

    print(i)

    threads = []

    x=0

    for t in range(0,3):

    t= threading.Thread(target=read_file,args=("D:/zhihu/",x))

    threads.append(t)

    x+=1

    #join在里面时候只有第一个子进程结束才能打开第二个进程,if__name__ 调用时不可用

    if __name__=="__main__":

    for thr in threads:

    thr.start()

    thr.join()

    print("all over %s"%ctime())

    就是把文件添加到线程池里面,再一起执行。

    下班了,这个代码对处理大量文件肯定有用,等会再更新吧..

    展开全文
  • 浅析linux环境下一进程最多有多少个线程默认情况下:主线程+辅助线程 +<253自己的线程含主线程和一辅助线程最多255,即你自己只能生成253个线程。据说可以设置线程数目:据说是可以设置的,但本人...

    浅析linux环境下一个进程最多能有多少个线程

    默认情况下:

    主线程+辅助线程 +<253个自己的线程

    含主线程和一个辅助线程,最多255个,即你自己只能生成253个线程。

    据说可以设置线程数目:

    据说是可以设置的,但本人还没有验证!不知道可否。

    在你的shell中键入limit命令,里面有descriptor的数目,就是你的进程可以创建的最大线程数目。可以通过修改内核文件更改该数目。

    时间: 2013-07-16

    =====最大线程数====linux 系统中单个进程的最大线程数有其最大的限制 PTHREAD_THREADS_MAX这个限制可以在 /usr/include/bits/local_lim.h 中查看对 linuxthreads 这个值一般是 1024,对于 nptl 则没有硬性的限制,仅仅受限于系统的资源这个系统的资源主要就是线程的 stack 所占用的内存,用 ulimit -s 可以查看默认的线程栈大小,一般情况下,这个值是 8M可以写一段简单的代码验证最多可以创建多少个线程 复制代码

    进程切换分两步: 1.切换页目录以使用新的地址空间 2.切换内核栈和硬件上下文 对于linux来说,线程和进程的最大区别就在于地址空间,对于线程切换,第1步是不需要做的,第2是进程和线程切换都要做的. 切换的性能消耗: 1.线程上下文切换和进程上下问切换一个最主要的区别是线程的切换虚拟内存空间依然是相同的,但是进程切换是不同的.这两种上下文切换的处理都是通过操作系统内核来完成的.内核的这种切换过程伴随的最显著的性能损耗是将寄存器中的内容切换出. 2.另外一个隐藏的损耗是上下文的切换会扰乱处理器的

    Linux中进程和线程的对比与区别 1.概念 进程:正在运行中的程序. 线程:进程中的一条执行路径. 2.区别 (1)通常在一个进程中可以包含若干个线程,它们可以利用进程所拥有的资源.在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位. (2)线程和进程的区别在于,子进程和父进程有不同的代码和数据空间,而多个线程则共享数据空间,每个线程有自己的执行堆栈和程序计数器为其执行上下文.多线程主要是为了节约CPU时间,发挥利用,根据具体情况而定.线程的

    pthread_kill: pthread_kill与kill有区别,是向线程发送signal.,大部分signal的默认动作是终止进程的运行,所以,我们才要用signal()去抓信号并加上处理函数. int pthread_kill(pthread_t thread, int sig); 向指定ID的线程发送sig信号,如果线程代码内不做处理,则按照信号默认的行为影响整个进程,也就是说,如果你给一个线程发送了SIGQUIT,但线程却没有实现signal处理函数,则整个进程退出. pthread

    linux下的C\C++多进程多线程编程实例详解 1.多进程编程 #include #include #include int main() { pid_t child_pid; /* 创建一个子进程 */ child_pid = fork(); if(child_pid == 0) { printf("child pid\n"); exit(0); } else { print

    一.在Linux下重启Oracle数据库及监听器: 方法1: 用root以ssh登录到linux,打开终端输入以下命令: cd $ORACLE_HOME #进入到oracle的安装目录 dbstart #重启服务器 lsnrctl start #重启监听器 cd $ORACLE_HOME #进入到oracle的安装目录 dbstart #重启服务器 lsnrctl start #重启监听器 ----------------------------------- 方法2: Sql代码 cd $OR

    本文实例讲述了JAVA多线程编程.分享给大家供大家参考,具体如下: 进程是系统进行资源调度和分配的一个独立单位. 进程的特点 独立性:进程是系统中独立存在的实体,拥有自己的独立资源和私有空间.在没有经过进程本身允许的情况下,不能直接访问其他进程. 动态性:进程与程序的区别在于,前者是一个正在系统中活动的指令,而后者仅仅是一个静态的指令集合 并发性:多个进程可以在单个处理器上并发执行,而不受影响. 并发性和并行性的区别: 并行性:在同一时刻,有多条指令在多个处理器上同时执行(多个CPU) 并发性:

    1>单线程或者单进程 相当于短链接,当accept之后,就开始数据的接收和数据的发送,不接受新的连接,即一个server,一个client 不存在并发. 2>循环服务器和并发服务器 1.循环服务器:一个server只能一次只能接收一个client,当当前client结束访问之后才能进行下一个client的连接. 2.并发服务器:一个server同一时间可以响应很多客户端的访问. 3>select+多线程模式 并发服务器的三种实现方式 1.多进程并发服务器 是指TCP连接后,每一个客户机的

    java多线程编程技术详解和实例代码 1.   Java和他的API都可以使用并发. 可以指定程序包含不同的执行线程,每个线程都具有自己的方法调用堆栈和程序计数器,使得线程在与其他线程并发地执行能够共享程序范围内的资源,比如共享内存,这种能力被称为多线程编程(multithreading),在核心的C和C++语言中并不具备这种能力,尽管他们影响了JAVA的设计. 2.   线程的生命周期 新线程的生命周期从"新生"状态开始.程序启动线程前,线程一直是"新生"状态:

    引言 java 7提供了另外一个很有用的线程池框架,Fork/Join框架 理论 Fork/Join框架主要有以下两个类组成. * ForkJoinPool 这个类实现了ExecutorService接口和工作窃取算法(Work-Stealing Algorithm).它管理工作者线程,并提供任务的状态信息,以及任务的执行信息 * ForkJoinTask 这个类是一个将在ForkJoinPool执行的任务的基类. Fork/Join框架提供了在一个任务里执行fork()和join()操作的机制

    15cb44ebecb8c6bc8ce4a86481136eb7.png

    Java 多线程优先级实例详解 线程的优先级将该线程的重要性传递给调度器.尽管CPU处理现有线程集的顺序是不确定的,但是调度器将倾向于让优先权最高的线程先执行. 你可以用getPriority()来读取现有线程的优先级,并且在任何时刻都可以通过setPriority()来修改优先级. import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class SimplePrio

    Python 多线程的实例详解 一)线程基础 1.创建线程: thread模块提供了start_new_thread函数,用以创建线程.start_new_thread函数成功创建后还可以对其进行操作. 其函数原型: start_new_thread(function,atgs[,kwargs]) 其参数含义如下: function: 在线程中执行的函数名     args:元组形式的参数列表.     kwargs: 可选参数,以字典的形式指定参数 方法一:通过使用thread模块中的函数创

    C++中Socket网络编程实例详解 现在几乎所有C/C++的后台程序都需要进行网络通讯,其实现方法无非有两种:使用系统底层socket或者使用已有的封装好的网络库.本文对两种方式进行总结,并介绍一个轻量级的网络通讯库ZeroMQ.  1.基本的Scoket编程 关于基本的scoket编程网络上已有很多资料,作者在这里引用一篇文章中的内容进行简要说明. 基于socket编程,基本上就是以下6个步骤: 1.socket()函数 2.bind()函数 3.listen().connect()函数 4

    JAVA 多线程爬虫实例详解 前言 以前喜欢Python的爬虫是出于他的简洁,但到了后期需要更快,更大规模的爬虫的时候,我才渐渐意识到Java的强大.Java有一个很好的机制,就是多线程.而且Java的代码效率执行起来要比python快很多.这份博客主要用于记录我对多线程爬虫的实践理解. 线程 线程是指一个任务从头至尾的执行流.线程提供了运行一个任务的机制.对于Java而言,可以在一个程序中并发地启动多个线程.这些线程可以在多处理器系统上同时运行. runnable接口 任务类必须实现runna

    展开全文
  • —-一进程可以开启的线程受可用内存限制,如果是32位的机器,那么默认一进程2G的可用内存,而每个线程默认分析1M的栈空间,所以这种情况下理论最线程数在2000多个。一解决办法是创建线程时减少线程栈的大小...
  • 可不可以在一进程内开130个线程?楼主invaders(不负少年头)2001-04-28 14:19:00 在 VC/MFC / 进程/线程/DLL 提问稳定吗?问题点数:86、回复次数:32Top1 楼ytweiwei(又穷又丑农村户口!!!!)回复于 2001-04-28 14:...
  • 因此,以32位Windows系统为例,每一进程的用户地址空间是2G,假如每个线程栈的大小是128K,最多16384(=210241024 / 128)个线程。实际在XP系统上,我发现大约能启动13000个线程。 然后,我认为,你的问题本质上...
  • 工作中碰到过这问题好几次了,觉得必要总结一下,所以了这篇文章,这篇文章分为三部分:认识问题、分析问题、解决问题。一、认识问题:首先我们通过下面这测试程序来认识这问题:运行的环境(必要说明...
  • 253自己的线程含主线程和一辅助线程最多255,即一用户只能生成253个线程。Linux最大线程数限制及当前线程数查询:1、总结系统限制:/proc/sys/kernel/pid_max #查系统支持的最大线程数,一般会很大,...
  • 其实他看这面经很问题,没有说明是什么操作系统,以及是多少位操作系统。 因为不同的操作系统和不同位数的操作系统,虚拟内存可能是不一样。 Windows 系统我不了解,我就说说 Linux 系统。 在 Linux 操作...
  • 第一部分 Java相关以及答案 ...由于细节内容实在太了,为了不影响文章的观赏性,只截出了一部分知识点大致的介绍一下,每小节点里面都更细化的内容! 需要这份文档的朋友可以帮忙点赞,点击下方神秘超链接,
  • Java虚拟机最多支持多少个线程?跟虚拟机开发商有关么?跟操作系统呢?还有其他的因素吗?Eddie的回答:这取决于你使用的CPU,操作系统,其他进程正在做的事情,你使用的Java的版本,...
  • 咱们都知道进程的概念,它是CPU分配资源的载体,是程序运行的实例;也知道线程的概念,它是程序执行的最小单元,是进程中的一实体用来执行程序,一进程....netLinux中最多能够有多少个进程?一.Linux中命...
  • 原标题:面试官:JVM最多支持多少个线程?皇叔6分钟速读仅需3分钟McGovernTheory在StackOverflow提了这样一问题:Java虚拟机最多支持多少个线程?跟虚拟机开发商有关么?跟操作系统呢?还有其他的因素吗?Eddie的...
  • python爬虫可以多少线程

    千次阅读 2021-01-14 03:20:29
    其实关于爬虫并没有明确多少数量开线程,因为这是无穷的,随着...一起来看下吧~1、爬虫环境Pycharm python3.7.02、进程和线程的关系:一个线程只能属于一进程,而一进程可以有多个线程,但至少个线程。资...
  • 最多能创建多少个TCP连接?

    千次阅读 多人点赞 2021-04-24 00:19:14
    低并发编程战略上藐视技术,战术上重视技术本文坚持看到结尾才动图气不气?我是一 Linux 服务器上的进程,名叫小进。老是人说我最多只能创建 65535 TCP 连接。我不信这邪...
  • 最多能创建多少个 TCP 连接?最多能创建多少个 TCP 连接?端口号资源文件描述符资源线程资源内存资源CPU资源总结 最多能创建多少个 TCP 连接? 首先,要清楚,创建一TCP连接需要四元组(源IP,源端口,目标...
  • Regular无限多线程 import threading import numpy as np import time class BuildThread(threading.Thread): def __init__(self, name, alist): threading.Thread.__init__(self) self.alist = alist def ...
  • 因此操作系统对一进程下的线程数量是限制的,不能无限的增多。经验值:3000-5000左右(我没有验证)。刚才说的是不考虑系统限制的情况,那如果考虑系统限制呢,主要跟以下几参数关系/proc...
  • 这是「进击的Coder」的第 400篇技术分享作者:闪客sun来源:低并发编程“ 阅读本文大概需要 13 分钟。 ”‍低并发编程战略上藐视技术,战术上重视技术本文坚持看到结尾才动图气不...
  • 原因是:“听说端口号最多有65535,那长连接就最多保持65535了”。是这样的吗?还有的人说:“应该受TCP连接里四元组的空间大小限制,算起来是200万亿!” 如果你对这问题也是理解的不够彻底,那么今天讲...
  • mid=2247489425&idx=1&sn=a13fba648173796e032b0e423f80e59f ...老是人说我最多只能创建 65535 TCP 连接。 我不信这邪,今天我要亲自去实践一下。 1.TCP连接的创建 我走到操作系统
  • 我使用多线程:在每目录(小任务)上创建一线程,使之工作:/*** Explore given directory.* @param dir - directory to explore.* @return snapshot of directory - FilesAndDirs object,* which encapsulates ...
  • 问题一(最多可以存储多少条数据)问题二 ,存储到最大之后再进行add会出现什么情况为什么(why?)ArrayList中add运行的代码块回过头来分析为什么数组越界业务中真的碰到了怎么解决链表形式存储(LinkedList)map分级...
  • 一台Java服务器能跑多少个线程?这问题来自一次线上报警如下图...但这数量应该是大了,我们都知道线程多了,就会有线程切换,带来性能开销。当时就想到一台java服务器到底可以多少个线程呢?跟什么关系?现...
  • if (l != null) throw new RuntimeException(...上述方法对Looper.class对象进行了加锁,这些加锁开销可能造成性能瓶颈。 没有更好的方法实现Looper.prepare()方法?看一看Android的中Looper的源码。 public clas

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 285,444
精华内容 114,177
关键字:

多线程最多可以有多少个