❶ linux 最多支持多少个线程
默认情况下:
主线程+辅助线程 +<253个自己的线程<=255
含主线程和一个辅助线程,最多255个,即一个用户只能生成253个线程。
Linux最大线程数限制及当前线程数查询:
1、总结系统限制有:
/proc/sys/kernel/pid_max #查系统支持的最大线程数,一般会很大,相当于理论值
/proc/sys/kernel/thread-max
max_user_process(ulimit -u) #系统限制某用户下最多可以运行多少进程或线程
/proc/sys/vm/max_map_count
硬件内存大小
2、java虚拟机本身限制:
-Xms #intial java heap size
-Xmx #maximum java heap size
-Xss #the stack size for each thread
3、查询当前某程序的线程或进程数
pstree -p `ps -e | grep java | awk '{print $1}'` | wc -l 或 pstree -p 3660 | wc -l
4、查询当前整个系统已用的线程或进程数
pstree -p | wc -l
1、 cat /proc/${pid}/status
2、pstree -p ${pid}
3、top -p ${pid} 再按H 或者直接输入 top -bH -d 3 -p ${pid}
top -H
手册中说:-H : Threads toggle
加上这个选项启动top,top一行显示一个线程。否则,它一行显示一个进程。
4、ps xH
手册中说:H Show threads as if they were processes
这样可以查看所有存在的线程。
5、ps -mp <PID>
手册中说:m Show threads after processes
这样可以查看一个进程起的线程数。
❷ Linux中,shell脚本如何使用信号机制去控制线程的开启关闭
trap是Linux的内建命令,用于捕捉信号,trap命令可以指定收到某种信号时所执行的命令。trap命令的格式如下:trap command sig1 sig2 ... sigN,当接收到sinN中任意一个信号时,执行command命令,command命令完成后继续接收到信号前的操作,直到脚本结束。利用trap命令捕捉INT信号(即与Ctrl+c绑定的中断信号)。trap还可以忽略某些信号,将command用空字符串代替即可,如trap "" TERM INT,忽略kill %n和Ctrl+c发送的信号(kill发送的是TERM信号)。Linux更强劲的杀死进程的命令:kill -9 进程号(或kill -9 %n作业号)等价与kill -KILL 进程号。
举个例子:
最近小A需要生产2015年全年的KPI数据报表,现在小A已经将生产脚本写好了,生产脚本一次只能生产指定一天的KPI数据,假设跑一次生产脚本需要5分钟,那么:
如果是循环顺序执行,那么需要时间:5 * 365 = 1825 分钟,约等于 6 天
如果是一次性放到linux后台并发执行,365个后台任务,系统可承受不住哦!
既然不能一次性把365个任务放到linux后台执行,那么,能不能实现自动地每次将N个任务放到后台并发执行呢?当然是可以的啦。
#!/bin/bash
source/etc/profile;
#-----------------------------
tempfifo=$$.fifo#$$表示当前执行文件的PID
begin_date=$1#开始时间
end_date=$2#结束时间
if[$#-eq2]
then
if["$begin_date">"$end_date"]
then
echo"Error!$begin_dateisgreaterthan$end_date"
exit1;
fi
else
echo"Error!Notenoughparams."
echo"Sample:shloop_kpi2015-12-012015-12-07"
exit2;
fi
#-----------------------------
trap"exec1000>&-;exec1000<&-;exit0"2
mkfifo$tempfifo
exec1000<>$tempfifo
rm-rf$tempfifo
for((i=1;i<=8;i++))
do
echo>&1000
done
while[$begin_date!=$end_date]
do
read-u1000
{
echo$begin_date
hive-fkpi_report.sql--hivevardate=$begin_date
echo>&1000
}&
begin_date=`date-d"+1day$begin_date"+"%Y-%m-%d"`
done
wait
echo"done!!!!!!!!!!"
第6~22行:比如:sh loop_kpi_report.sh 2015-01-01 2015-12-01:
$1表示脚本入参的第一个参数,等于2015-01-01
$2表示脚本入参的第二个参数,等于2015-12-01
$#表示脚本入参的个数,等于2
第13行用于比较传入的两个日期的大小,>是转义
第26行:表示在脚本运行过程中,如果接收到Ctrl+C中断命令,则关闭文件描述符1000的读写,并正常退出
exec 1000>&-;表示关闭文件描述符1000的写
exec 1000<&-;表示关闭文件描述符1000的读
trap是捕获中断命令
第27~29行:
第27行,创建一个管道文件
第28行,将文件描述符1000与FIFO进行绑定,<读的绑定,>写的绑定,<>则标识对文件描述符1000的所有操作等同于对管道文件$tempfifo的操作
第29行,可能会有这样的疑问:为什么不直接使用管道文件呢?事实上这并非多此一举,管道的一个重要特性,就是读写必须同时存在,缺失某一个操作,另一个操作就是滞留,而第28行的绑定文件描述符(读、写绑定)正好解决了这个问题
第31~34行:对文件描述符1000进行写入操作。通过循环写入8个空行,这个8就是我们要定义的后台并发的线程数。为什么是写空行而不是写其它字符?因为管道文件的读取,是以行为单位的
第37~42行:
第37行,read -u1000的作用就是读取管道中的一行,在这里就是读取一个空行;每次读取管道就会减少一个空行
第39~41行,注意到第42行结尾的&吗?它表示进程放到linux后台中执行
第41行,执行完后台任务之后,往文件描述符1000中写入一个空行。这是关键所在了,由于read -u1000每次操作,都会导致管道减少一个空行,当linux后台放入了8个任务之后,由于文件描述符1000没有可读取的空行,将导致read -u1000一直处于等待。
❸ Linux线程及同步
linux多线程
1.线程概述
线程是一个进程内的基本调度单位,也可以称为轻量级进程。线程是在共享内存空间中并发的多道执行路径,它们共享一个进程的资源,如文件描述和信号处理。因此,大大减少了上下文切换的开销。一个进程可以有多个线程,也就
是有多个线程控制表及堆栈寄存器,但却共享一个用户地址空间。
2.线程实现
线程创建pthread_create()
所需头文件#include
<pthread.h>
函数原型int
pthread_create
((pthread_t
*thread,
pthread_attr_t
*attr,
thread:线程标识符
attr:线程属性设置
start_routine:线程函数的起始地址
arg:传递给start_routine的参数
函数返回值
成功:0
出错:-1
线程退出pthread_exit();
所需头文件#include
<pthread.h>
函数原型void
pthread_exit(void
*retval)
函数传入值retval:pthread_exit()调用者线程的返回值,可由其他函数如pthread_join
来检索获取
等待线程退出并释放资源pthread_join()
所需头文件#include
<pthread.h>
函数原型int
pthread_join
((pthread_t
th,
void
**thread_return))
函数传入值
th:等待线程的标识符
thread_return:用户定义的指针,用来存储被等待线程的返回值(不为NULL时)
函数返回值
成功:0
出错:-1
代码举例
1.
#include<pthread.h>
2.
#include<stdio.h>
3.
#include<errno.h>
4.
5.
/*线程1*/
6.
void
thread1()
7.
{
8.
int
i=0;
9.
10.
while(1)
11.
{
12.
printf(thread1:%d/n,i);
13.
if(i>3)
14.
pthread_exit(0);
15.
i++;
16.
sleep(1);
17.
}
18.
}
19.
20.
/*线程2*/
21.
void
thread2()
22.
{
23.
int
i=0;
24.
25.
while(1)
26.
{
27.
printf(thread2:%d/n,i);
28.
if(i>5)
29.
pthread_exit(0);
30.
i++;
31.
sleep(1);
32.
}
33.
}
34.
35.
int
main()
36.
{
37.
pthread_t
t1,t2;
38.
39.
/*创建线程*/
40.
pthread_create(&t1,NULL,(void
*)thread1,NULL);
41.
pthread_create(&t2,NULL,(void
*)thread2,NULL);
42.
/*等待线程退出*/
43.
pthread_join(t1,NULL);
44.
pthread_join(t2,NULL);
45.
return
0;
46.
}
3同步与互斥
<1>互斥锁
互斥锁的操作主要包括以下几个步骤。
•
互斥锁初始化:pthread_mutex_init
•
互斥锁上锁:pthread_mutex_lock
•
互斥锁判断上锁:pthread_mutex_trylock
•
互斥锁接锁:pthread_mutex_unlock
•
消除互斥锁:pthread_mutex_destroy
1.
#include<pthread.h>
2.
#include<stdio.h>
3.
#include<errno.h>
4.
5.
int
i=0;/*共享变量*/
6.
pthread_mutex_t
mutex=PTHREAD_MUTEX_INITIALIZER;/*互斥锁*/
7.
8.
void
thread1()
9.
{
10.
int
ret;
11.
while(1)
12.
{
13.
14.
15.
ret=pthread_mutex_trylock(&mutex);/*判断上锁*/
16.
17.
if(ret!=EBUSY)
18.
{
19.
pthread_mutex_lock(&mutex);/*上锁*/
20.
printf(This
is
thread1:%d/n,i);
21.
i++;
22.
pthread_mutex_unlock(&mutex);/*解锁*/
23.
}
24.
sleep(1);
25.
}
26.
}
27.
28.
void
thread2()
29.
{int
ret;
30.
while(1)
31.
{
32.
33.
ret=pthread_mutex_trylock(&mutex);
34.
if(ret!=EBUSY)
35.
{
36.
pthread_mutex_lock(&mutex);
37.
printf(This
is
thread2:%d/n,i);
38.
i++;
39.
pthread_mutex_unlock(&mutex);
40.
}
41.
sleep(1);
42.
}
43.
}
44.
int
main()
45.
{
46.
pthread_t
t1,t2;
47.
pthread_mutex_init(&mutex,NULL);
48.
pthread_create(&t1,NULL,(void
*)thread1,NULL);
49.
pthread_create(&t2,NULL,(void
*)thread2,NULL);
50.
51.
pthread_join(t1,NULL);
52.
pthread_join(t2,NULL);
53.
54.
pthread_mutex_destroy(&mutex);
55.
return
0;
56.
}
<2>信号量
未进行同步处理的两个线程
1.
#include<pthread.h>
2.
#include<stdio.h>
3.
#include<errno.h>
4.
5.
int
i=0;
6.
void
thread1()
7.
{
8.
9.
while(1)
10.
{
11.
printf(This
is
thread1:%d/n,i);
12.
i++;
13.
sleep(1);
14.
}
15.
}
16.
17.
18.
void
thread2()
19.
{
20.
21.
while(1)
22.
{
23.
printf(This
is
thread2:%d/n,i);
24.
i++;
25.
sleep(1);
26.
}
27.
}
28.
29.
int
main()
30.
{
31.
pthread_t
t1,t2;
32.
33.
pthread_create(&t1,NULL,(void
*)thread1,NULL);
34.
pthread_create(&t2,NULL,(void
*)thread2,NULL);
❹ Linux 线程同步有哪些方法
一、互斥锁(mutex)
1.
初始化锁。在Linux下,线程的互斥量数据类型是pthread_mutex_t。在使用前,要对它进行初始化。
静态分配:pthread_mutex_t
mutex
=
PTHREAD_MUTEX_INITIALIZER;
动态分配:int
pthread_mutex_init(pthread_mutex_t
*mutex,
const
pthread_mutex_attr_t
*mutexattr);
2.
加锁。对共享资源的访问,要对互斥量进行加锁,如果互斥量已经上了锁,调用线程会阻塞,直到互斥量被解锁。
int
pthread_mutex_lock(pthread_mutex
*mutex);
int
pthread_mutex_trylock(pthread_mutex_t
*mutex);
3.
解锁。在完成了对共享资源的访问后,要对互斥量进行解锁。
int
pthread_mutex_unlock(pthread_mutex_t
*mutex);
4.
销毁锁。锁在是使用完成后,需要进行销毁以释放资源。
int
pthread_mutex_destroy(pthread_mutex
*mutex);
二、条件变量(cond)
1.
初始化条件变量。
静态态初始化,pthread_cond_t
cond
=
PTHREAD_COND_INITIALIER;
动态初始化,int
pthread_cond_init(pthread_cond_t
*cond,
pthread_condattr_t
*cond_attr);
2.
等待条件成立。释放锁,同时阻塞等待条件变量为真才行。timewait()设置等待时间,仍未signal,返回ETIMEOUT(加锁保证只有一个线程wait)
int
pthread_cond_wait(pthread_cond_t
*cond,
pthread_mutex_t
*mutex);
int
pthread_cond_timewait(pthread_cond_t
*cond,pthread_mutex
*mutex,const
timespec
*abstime);
3.
激活条件变量。pthread_cond_signal,pthread_cond_broadcast(激活所有等待线程)
int
pthread_cond_signal(pthread_cond_t
*cond);
int
pthread_cond_broadcast(pthread_cond_t
*cond);
//解除所有线程的阻塞
4.
清除条件变量。无线程等待,否则返回EBUSY
int
pthread_cond_destroy(pthread_cond_t
*cond);
三、信号量(sem)
1.
信号量初始化。
int
sem_init
(sem_t
*sem
,
int
pshared,
unsigned
int
value);
这是对由sem指定的信号量进行初始化,设置好它的共享选项(linux
只支持为0,即表示它是当前进程的局部信号量),然后给它一个初始值VALUE。
2.
等待信号量。给信号量减1,然后等待直到信号量的值大于0。
int
sem_wait(sem_t
*sem);
3.
释放信号量。信号量值加1。并通知其他等待线程。
int
sem_post(sem_t
*sem);
4.
销毁信号量。我们用完信号量后都它进行清理。归还占有的一切资源。
int
sem_destroy(sem_t
*sem);
❺ linux 多线程环境下的几种锁机制
NO1
互斥量(Mutex)
互斥量是实现最简单的锁类型,因此有一些教科书一般以互斥量为例对锁原语进行描述。互斥量的释放并不仅仅依赖于释放操作,还可以引入一个定皮返时器属性。如果在释放操作执行前发生定时器超时,则互斥量也会释放代码块或共享存储区供其他线程访问。当有异常发生时,可使用try-finally语句来确保互斥量被释放。定时器状态或try-finally语句的使用可以避免产生死锁。
递归锁(Recursive
Lock)
递归锁是指可以被当前持有该锁的线程重复获取,而不会导致该线程产生死锁的锁类型。对递归锁而言,只有在当前持有线程的获取锁操作都有一个释放操作与之对应时,其他线程才可以获取该锁。因此,在使用递归锁时,必须要用足够的释放锁操作来平衡获取锁操作,实现这一目标的最佳方式是在单入口单出口代码块的两头一一对应地使用获取、释放操作,做法和在普通锁中一样。递归锁在递归函数中最有用。但是,总的来说,递归锁比非递归锁速度要慢。需要注意的是:调用线程获得几次递归锁必须释放几次递归锁。
以下为一个递归锁的示例:
[cpp] view plain
Recursive_Lock L
void recursiveFunction (int count) {
L->acquire()
if (count > 0) {
count = count - 1;
recursiveFunction(count);
}
L->release();
}
读写锁(Read-Write
lock) 读写锁又称为共享独占锁(shared-exclusive
lock)、多读单写锁(multiple-read/single-write lock)或者非互斥信号量(non-mutual
exclusion
semaphore)。读写锁允许多个线程同时进行读访问,但是在某一时刻却最多只能由一个线程执行写操作。对于多个线程需要同时读共享数据却并不一定进行写操作的应用来说,读写锁是一种高效的同步机制。对于较长的共享数据,只为其设置一个读写锁会导致较长的访问时间,最好将其划分为多个小段并设置多个读写锁以进行同步。
这个读写锁我们在学习数据库的时候应该很熟悉的哟!
旋转锁(Spin
Lock)
旋转锁是一种非阻塞锁,由某个线程独占。腔指采伍握配用旋转锁时,等待线程并不静态地阻塞在同步点,而是必须“旋转”,不断尝试直到最终获得该锁。旋转锁多用于多处理器系统中。这是因为,如果在单核处理器中采用旋转锁,当一个线程正在“旋转”时,将没有执行资源可供另一释放锁的线程使用。旋转锁适合于任何锁持有时间少于将一个线程阻塞和唤醒所需时间的场合。线程控制的变更,包括线程上下文的切换和线程数据结构的更新,可能比旋转锁需要更多的指令周期。旋转锁的持有时间应该限制在线程上下文切换时间的50%到100%之间(Kleiman,1996年)。在线程调用其他子系统时,线程不应持有旋转锁。对旋转锁的不当使用可能会导致线程饿死,因此需谨慎使用这种锁机制。旋转锁导致的饿死问题可使用排队技术来解决,即每个等待线程按照先进先出的顺序或者队列结构在一个独立的局部标识上进行旋转。
学习了这些,果然受益匪浅,在今后的coding中,我得挨个试试咯。
❻ linux进程、线程及调度算法(三)
调度策略值得是大家都在ready时,并且CPU已经被调度时,决定谁来运行,谁来被调度。
两者之间有一定矛盾。
响应的优化,意味着高优先级会抢占优先级,会花时间在上下文切换,会影响吞吐。
上下文切换的时间是很短的,几微妙就能搞定。上下文切换本身对吞吐并多大影响, 重要的是,切换后引起的cpu 的 cache miss.
每次切换APP, 数据都要重新load一次。
Linux 会尽可能的在响应与吞吐之间寻找平衡。比如在编译linux的时候,会让你选择 kernal features -> Preemption model.
抢占模型会影响linux的调度算法。
所以 ARM 的架构都是big+LITTLE, 一个很猛CPU+ 多个 性能较差的 CPU, 那么可以把I/O型任务的调度 放在 LITTLE CPU上。需要计算的放在big上。
早期2.6 内核将优先级划分了 0-139 bit的优先级。数值越低,优先级越高。0-99优先级 都是 RT(即时响应)的 ,100-139都是非RT的,即normal。
调度的时候 看哪个bitmap 中的 优先级上有任务ready。可能多个任务哦。
在普通优先级线程调度中,高优先级并不代表对低优先级的绝对优势。会在不同优先级进行轮转。
100 就是比101高,101也会比102高,但100 不会堵着101。
众屌丝进程在轮转时,优先级高的:
初始设置nice值为0,linux 会探测 你是喜欢睡眠,还是干活。越喜欢睡,linux 越奖励你,优先级上升(nice值减少)。越喜欢干活,优先级下降(nice值增加)。所以一个进程在linux中,干着干着 优先级越低,睡着睡着 优先级越高。
后期linux补丁中
红黑树,数据结构, 左边节点小于右边节点
同时兼顾了 CPU/IO 和 nice。
数值代表着 进程运行到目前为止的virtual runtime 时间。
(pyhsical runtime) / weight * 1024(系数)。
优先调度 节点值(vruntime)最小的线程。权重weight 其实有nice 来控制。
一个线程一旦被调度到,则物理运行时间增加,vruntime增加,往左边走。
weight的增加,也导致vruntime减小,往右边走。
总之 CFS让线程 从左滚到右,从右滚到左。即照顾了I/O(喜欢睡,分子小) 也 照顾了 nice值低(分母高).所以 由喜欢睡,nice值又低的线程,最容易被调度到。
自动调整,无需向nice一样做出奖励惩罚动作,个人理解权重其实相当于nice
但是 此时 来一个 0-99的线程,进行RT调度,都可以瞬间秒杀你!因为人家不是普通的,是RT的!
一个多线程的进程中,每个线程的调度的策略 如 fifo rr normal, 都可以不同。每一个的优先级都可以不一样。
实验举例, 创建2个线程,同时开2个:
运行2次,创建两个进程
sudo renice -n -5(nice -5级别) -g(global), 会明显看到 一个进程的CPU占用率是另一个的 3倍。
为什么cpu都已经达到200%,为什么系统不觉得卡呢?因为,我们的线程在未设置优先级时,是normal调度模式,且是 CPU消耗型 调度级别其实不高。
利用chrt工具,可以将进程 调整为 50 从normal的调度策略 升为RT (fifo)级别的调度策略,会出现:
chrt , nice renice 的调度策略 都是以线程为单位的,以上 设置的将进程下的所有线程进行设置nice值
线程是调度单位,进程不是,进程是资源封装单位!
两个同样死循环的normal优先级线程,其中一个nice值降低,该线程的CPU 利用率就会比另一个CPU的利用率高。