Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1814449
  • 博文数量: 438
  • 博客积分: 9799
  • 博客等级: 中将
  • 技术积分: 6092
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-25 17:25
文章分类

全部博文(438)

文章存档

2019年(1)

2013年(8)

2012年(429)

分类: 服务器与存储

2012-03-26 09:07:56

操作系统里有进程(Process)的概念。进程是一个拥有独立的虚拟空间,独一无二的id和资源(文件、socket)句柄的可以在CPU中执行的程序。操作系统为每个进程分配一个时间片(time slice),快速地在各进程中切换,达到各程序并行的效果。

 

每个进程多数情况下并不能完整地用完所得到的时间片(因为中断,比如I/O)而不得不放弃对CPU的使用权,所以线程(Thread)的概念由此诞生。当 进程中的某个线程等待I/O操作时,另外的线程可以继续运行,直到用完所得到的时间片。对进程来说,CPU的资源就像钱对人类一样珍贵。

 

随着硬件技术的发展,多核和多处理器技术可以让线程真正地并发运行。

 

线程共享进程中的内存空间以及资源,但拥有独立的栈。线程的实质是保存在寄存器中的一些状态——程序指针(IP)和栈指针(SP)。当线程切换时,发生改 变的仅仅是这两个存放在寄存器中的指针值,被激活的线程便根据新的IP值开始执行程序,并始用当前SP所指的栈,所以线程切换是非常迅速的。相对而言,进 程的切换非常慢,因为进程的切换要切换虚拟内存空间,导致页表切换。

 

线程在内存中只有栈是私有的,有时它需要在栈外维护一个私有存储空间(Thread Local Storage, TLS),比如全局变量。Windows下可以调用TlsAlloc来获得这块存储区域。相关的函数分别是:TlsAlloc反回一个 index,TlsGetValue(index)返回这个存储区里的值,TlsSetValue(index, value)设置存储的值,TlsFree(index)释放这个存储区(注:这个函数会释放会令这个index在所有线程中失效,所以要确保最后退出进 程的线程调用该函数)。另一种在栈外存储私有数据的方式是私有堆(Private Heap),相关的Windows函数HeapCreate, HeapAlloca, HeapFree和HeapDestory。

 

线程同步

 

Mutex:当线程获得Mutex时,其它尝试得到这个Mutex的线程都会被挂起,直到持有这个Mutex的线程释放该Mutex;

 

Semaphore: Mutex的进化版,通过计数允许多个线程获得这个资源,而不像Mutex一次只能被一个线程获得。当获取Semaphore的线程数达到最大值时,则尝试获取Semaphore的线程就会挂起,直到有线程释放Semaphore。

 

Event: 当某个Event发生时,唤醒等待该事件的线程。有两种方式:Sychronization和Notification。前者只唤醒最先等待该事件的线程,后者唤醒所有待待该事件的线程。

 

Waitable Timer: 相当于Time Event,当计时器时间到后,就会唤醒线程。

 

Critical Section: 类似于Mutex,当一个线程进入临界区(enter critical section)后,其它尝试进入的线程都会被挂起,直到该线程离开临界(leave critical section)区。与Mutex不同,进入临界区的线程再次进入临界区(在递归函数里会出现这种情况),而Lock Mutex的线程再试尝试Lock同一个Mutex,会造成死锁(Dead Lock)。

 

Slim Reader/Writer: 当某个线程尝试读操作,如果有别的线程正在进行写操作,则挂起;否则即使有别的线程正在进行读操作。当线程尝试写操作,只要有别的线程在进行读操作或写操作,则挂起。

 

Condition Variables: 这个Variable必须与Mutex或Critical Section一起使用。当一个线程获得了一个Mutex或进入了临界区,当它block住(比如等待I/O)时,可以放弃这个Mutex或离开临界区, 同时挂起;当block这个线程的原因消失后(比如I/O操作完成),并且Mutex或临界区没有被其它线程占用,它可以重新获得这个Mutex或临界 区。

 

 

同步造成的问题

Starvation:某一线程可能无法得到Mutex而始终无法运行。比如Priority太低Mutex总是被更高优先级的线程占据。

 

Dead lock:当两个线程同时占有对方想要的Mutex时,会导致死锁。比如一个线程得到了A,等待B,而另一线程得到了B,等待A,这时两个线程因为得不到想要的Mutex,而无法运行下去。

 

Live lock:解决Dead lock可以有一个timeout的方法。当一个线程等待一个Mutex超时后,释放掉自己占有的Mutex。这种方案可能会出现这种情况:线程获得A, 等待B超时后释放A;而另一个线程获得B,等待A超时释放B。之后两个线程又同时获得了对方想要的Mutex,等待超时后又再次释放已获得的Mutex。 如此反复。

 

Interlocked Operations

 

有些原子操作是可以在用户模式(User Mode)下完成的,从而省去了进入操作系统内核的开销,更高效:

 

Interlocked[In|De]crement(&a) <==> a++ 或 a--

InterlockedAdd(&a, b) <==> a += b

InterlockedExchanged(&a, b) <==> a = b

Interlocked[xor|and|or](&a, b) <==> a ^= b 或 a &= b 或 a |= b


阅读(1640) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~