- UID
- 1029342
- 性别
- 男
|
四、进程及线程的比较以及注意事项线程是多线程编程中的主编程接口。线程仅在进程内部是可见的,进程内部的线程会共享诸如地址空间、打开的文件等所有进程资源。由于线程可共享进程指令和大多数进程数据,因此一个线程对共享数据进行的更改对进程内其他线程是可见的。一个线程需要与同一个进程内的其他线程交互时,该线程可以在不涉及操作系统的情况下进行此操作。
进程是操作系统分配资源的单位,不同的进程拥有的资源不同,比如地址空间、打开的文件等等。
1.多线程编程的特殊之处因为多线程共享进程的大多数数据,因此也引入了新的注意事项:
- 线程安全函数:在C语言中局部变量是在栈中分配的,任何未使用静态数据或全局数据的函数都是线程安全的。非线程安全的函数可以通过加锁的方式来使函数实现线程安全。
- 线程安全的(Thread-Safe):如果一个函数在同一时刻可以被多个线程安全地调用,就称该函数是线程安全的。线程安全函数解决多个线程调用函数时访问共享资源的冲突问题。
- 可重入(Reentrant):函数可以由多于一个线程并发使用,而不必担心数据错误。可重入函数可以在任意时刻被中断,稍后再继续运行,不会丢失数据。可重入性解决函数运行结果的确定性和可重复性。
可重入函数编写规范:
- 不在函数内部使用静态或全局数据
- 不返回静态或全局数据,所有数据都由函数的调用者提供。
- 使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据。
- 如果必须访问全局变量,利用互斥机制来保护全局变量。
- 不调用不可重入函数。
线程安全与可重入函数之间的关系:
- 一个函数对于多个线程是可重入的,则这个函数是线程安全的。
- 一个函数是线程安全的,但并不一定是可重入的。【例如可以使用互斥锁实现的线程安全】
- 可重入性要强于线程安全性。
例如标准库中的malloc是不可重入的,但是标准库的实现一般都有提供线程安全的版本(具体怎么用线程安全的版本可以查看使用的库的手册,总之小心一点)。
线程安全和可重入函数针对的是多线程环境下,由多个线程共享的资源(静态数据、全局数据)的使用问题。之所以有这个问题是因为在某些函数会使用静态或者全局数据,它运行的结果依赖于这些数据,这时如果此类函数在运行中被切换走,然后另一个线程也调用了该函数就会出问题,因此这类问题是特定存在于多线程环境的(当然类似的问题在进程环境下也存在,存在的原因是信号处理函数可能在任意时刻被调用,因而它可能打断进程的正常运行;多个进程之间是不存在这类问题的,因为每个进程看到的都是自己独立的地址空间)。
2.同步和互斥的实现
- 多线程使用互斥锁、条件变量、自旋锁、读写锁和信号量来实现同步和互斥。
- 多进程也使用互斥锁、条件变量、自旋锁、读写锁和信号量来实现同步和互斥,但是需要用进程间通信(IPC)来实现信息共享/传输。
需要注意的是,并没有所谓的线程间通信,因为同一个进程内部的线程会共享该进程的资源,比如堆空间、打开的文件等等,因而同一个进程内的多个线程之间不存在通信问题;如果是不同进程之间的线程,就按照进程之间通信方式进行通信即可。
3.何时采用多线程何时采用多进程这是一个比较艰难的抉择,取决于应用场景,它们的区别或许是有参考价值的:
- 一个进程中的所有线程都必须运行相同的可执行程序(它们最多能做到的是运行相同可执行程序的不同部分)。而一个子进程可以运行一个完全不同的可执行程序。
- 进程是操作系统分配资源的基本单位,而线程则不是,线程独立拥有的主要是栈和线程专用缓冲区
- 由于同一个进程内的多个线程共享同一块虚拟内存和其它资源,因而一个线程出错可能会影响到同一个进程中的其它线程。而多进程环境下,一个进程出错并不会影响其它进程,因为每一个进程都拥有自己独立的资源。
- 创建新进程时的资源拷贝使得创建新的进程比创建新的线程效率底下很多。不过由于写时拷贝机制的存在,因而如果子进程不产生写请求,这个影响就会很小。
- 如果一个任务可以被分解为多个几乎完全相同的子任务,则多线程就可能是一个很好的选择。
- 由于同一个进程内的多个线程共享进程的资源,因而多个线程共享资源非常简单(当然,代价是必须防止出现竞态)。而多进程之间共享资源则需要通过IPC机制。
|
|