为啥要搞多线程?因为多进程太耗费资源!一个进程包含(数据区,代码区,堆栈区)。而一个线程只有(堆栈区)。
为啥要“多”xx程序设计!想想看,你用浏览器打开某一个网站的网页,可以这个网站的服务器坏了。如果你的浏览器是单线程的,你就得一直等待这个链接connect返回失败才能继续连接别的网页或者干其他的事情。 所以你可以为每一个连接创建一个线程,就算是打不开网页,你的主线程(WIN下面好像都是线程)或者主程序还可以干自己的事情。
多线程会不会提高程序效率?从上面的例子看是的!但是不要误解多线程的程序肯定就快!
如果你的程序只是简单的递增一个简单的变量a的值,你创建10个线程分别递增a,和只是简单的一个主程序递增是没有区别的!多线程有可能会更慢,因为线程之间的调度会浪费时间。
单核的CPU,是不是没必要写多线程程序?假设一个进程A一秒钟有1/5的时间是运行它。A创建多个线程并不会增加A进程的CPU运行时间。
对于上面那个例子
1 connect 服务器
2 do something.
如果是单线程的程序,那么程序就阻塞在1步骤上了,1/5的CPU时间就被浪费了一部分。
如果多线程呢?
像这样
1 create threadconnect ;
2 do something;
然后CPU分别执行 do something 和 threadconnect。由于CPU是单核的,所以不存在并行执行,他们两个操作本来是要均分1/5的CPU时间的(看OS具体实现了)。但是由于threadconnect被阻塞,该线程会睡眠,所以CPU会尽量多运行 do something。这个时候,你不用等待threadconnect完成就可以干自己的事情。等connect返回后,这两个线程又会均分进程空间的时间了。但是你的程序没有空转,所以效率高了!
单线程当然也可以达到多线程的效果
比如还是那个例子:
while(true)
{
1 connect;
2 do something;
}
这里第一步的区别是 不等待connect的返回,直接执行第二步,然后每次用while来查看connect有没有连接好,如果没连接好,继续做自己的事情。程序也没有空转。就是选择非阻塞IO。具体的程序设计起来会更复杂,这里只是为了说明道理!
多线程编程的另一个难点是 对全局资源的使用。
例如一个全局变量 a, 有一个线程A对a加1,一个线程B对a减2。
如下:
A线程: a+=1; B线程: a-=2;
等价 1 mov ax,a 等价 1 mov ax,a
2 add ax,1 2 sub ax,2
3 mov a,ax 3 mov a,ax
假设 a=2一开始。
线程A运行完2时,切换到了B线程.此时B线程操作的数据还是内存中的a=2.然后等B线程运行完,a=0,切换到A,然后把ax中的3放入内存中的变量a中。这样就把B线程的结果给覆盖掉了。看来这样不加以限制 A,B线程,会导致很严重的后果---程序结果就变得不确定了。
最好的方法就是 加锁 LOCK;
程序变成这样
A线程 : LOCK B线程 LOCK
a+=1; a-=2;
UNLOCK UNLOCK
这样 a+=1和 a-=2就变成 原子操作=》不可分割的操作。
就算是 A线程在运行完 add ax,1后 切换到 B线程,但是由于 A已经LOCK了,所以B线程只能干等!然后程序又切换到A线程,A线程得以运行完!
a+=1;a-=2; 这两行代码 又叫做 临界区。
看来线程对 临界区的操作 要LOCK(其实就是修改全局变量就LOCK)。
看来多线程编程相当简单了。
举个例子: 全局变量 List a; 有两个操作 addnode(list& a) delnode(list& a)
main()
{
create ThreadA=FUNCITON1;
create ThreadB=FUNCITON2;
}
FUNCITON1 FUNCTION2
{ {
LOCK; LOCK;
addnode(a); delnode(a);
UNLOCK; UNLOCK;
} }
以上就是多线程编程的模板,不管是UNIX还是WIN下 都一样。
还有一个问题 LOCK是什么?看看0.12Linux Kernel怎么写的吧
void lock() void unlock()
{ {
cli()关中断; cli() 关中断;
while(lock==1) lock=0;
sleep(); wake(lock);
lock=1; sti() 开中断;
sti()开中断; }
}
为什么要 开关中断? 很明显为了上面叙述过的 原子操作!
为啥要sleep和wake;
如果一个线程A得不到 LOCK 还一味的试探 lock是否为0,那么程序会空转!如果A睡觉,那么A的时间会让给其他线程,如B。
为啥要wake? A已经睡觉了,贡献了自己的运行时间,B运行完得唤醒我,让我运行啊!来而不往非礼也! 这个职业的说法叫 信号量 (应该说是原理,提出者是狄伊克斯特拉,是个应该被大家记住的名字)
现在时多核时代!!!简单的开关中断显然不行。因为运行在其他核心的线程B,仍然有可能进入临界区!
看来需要寻求硬件的帮助了!
好吧 TSL 指令发明出来了。
TSL 寄存器register,锁lock
原理: TSL指令 把内存中的LOCK值读取到寄存器register中,然后在内存中的LOCK中存一个非0值。这两个操作很明显要 原子操作。
执行TSL指令的CPU会锁住内存总线,以禁止其他CPU在本指令结束之前访问内存。
改写新的LOCK
LOCK:
TSL register,lock
cmp register ,0 //如果lock为0的话,就取得了锁,然后RET返回。反之则忙
jne enter_region//等待,这里可以换成call otherfunction 以避免忙等待
ret
UNLOCK:
mov lock,0
ret
锁的问题看来搞的差不多了。
以上的内容超出了 多线程编程的 内容,多线程编程只需要套模板,当然API有很多。但是掌握了这些理论知识。通吃你多线程还是多进程。
浅谈多线程编程
最新推荐文章于 2024-04-28 05:00:00 发布