前言
多线程编程是一种并发编程模式,它允许程序同时执行多个线程,从而提高程序的并发性和性能。多线程编程可以在单个应用程序中同时执行多个任务,从而提高应用程序的吞吐量和响应速度。如果我们想在以后的编程生涯中,更好的掌握相关知识,我们必须掌握《操作系统》中的相关知识,为我们以后的发展打好基础。
这里面所有涉及到的都是《操作系统》的基础知识,我这里只进行个人的简单整理。
多道程序的执行是“随机”的
计算机硬件系统和操作系统将 CPU 时间分片,轮流执行多个程序。这就需要在某一个程序所持有的时间片段用完后,选择其他的程序执行。而这个选择过程是操作系统的“调度程序”完成的。
如果一个程序需要输入数据,或者打印数据,由于输入设备(包括人的输入过程)相对 CPU 速度而言是极慢的,在输入或打印没有完成前,这些程序无法执行后面的代码。在这种情况下,如果任由它们慢慢完成输入输出任务,而且还要占用时间片段,那么,就会出现“浪费“CPU的情况。
这种情况下,操作系统往往会让这些程序处于“休眠”的状态,不再参与CPU的“竞争”,等到输入输出任务完成后,再“唤醒"它们,再次让它们参与到轮流申请CPU的行列中。这样,就可以把CPU时间更多地分配给其它程序从而大大提高CPU整体利用率。因此,多个程序也就不再以固定的顺序轮流执行了。
多道程序的调度是的每一个程序的执行(占有CPU)变得很”随机“。
但真正在计算机中,多道程序的执行也并不是随机的。多道程序是一种计算机操作系统的并发执行模式,它允许多个程序同时运行,从而提高计算机的利用效率。在多道程序环境下,操作系统会按照一定的算法和调度策略,将CPU时间分配给不同的程序,以实现多个程序的并发执行。在多道程序的执行过程中,操作系统会根据进程的优先级、进程等待的时间、进程的执行状态等因素,对多个进程进行调度,决定哪个进程可以获得CPU的使用权,以及使用多长时间。这个调度过程也是按照一定的规则进行的。
进程
在计算机操作系统中,进程是指正在运行的程序的实例。一个进程包含了程序代码、数据和程序执行的状态信息,它是计算机操作系统进行资源分配和调度的基本单位。
每个进程都有自己的内存空间、寄存器集合、栈、堆等资源,这些资源在进程执行期间被操作系统动态地分配和管理。进程可以通过系统调用创建、终止、挂起和恢复等操作。
进程的概念是为了实现多任务操作系统而产生的。在多任务操作系统中,多个进程可以在同一时间内并发地运行,每个进程都有自己的执行时间片,操作系统按照一定的调度算法分配时间片,使得多个进程可以交替执行,从而实现并发执行的效果。
进程也是多道程序设计的基础,多道程序设计是指在计算机系统中,允许多个程序同时存在于内存中,并且在同一时间内可以并发地执行。在多道程序设计中,每个程序都被认为是一个进程,操作系统会通过进程调度算法,按照一定的顺序分配CPU时间片给各个进程,从而实现多个进程的并发执行。
进程的管理和调度
进程的管理和调度是操作系统的重要功能之一。操作系统需要对进程进行创建、终止、挂起、恢复等操作,并且需要按照一定的调度算法分配CPU时间片,实现多个进程的并发执行。
下面是进程管理和调度的基本流程:
- 进程创建:当用户发起一个程序运行请求时,操作系统会创建一个新的进程,并为其分配必要的资源,如内存空间、寄存器等。
- 进程终止:当一个进程完成了它的任务或者出现了错误时,操作系统会终止该进程,并释放它占用的资源。
- 进程挂起和恢复:当一个进程需要等待某个事件的发生(如等待I/O操作完成),操作系统会将该进程挂起,直到事件发生后再恢复该进程的执行。
- 进程调度:操作系统会按照一定的调度算法选择要执行的进程,并分配CPU时间片,使得多个进程可以交替执行。
常见的进程调度算法包括:
- 先来先服务(FCFS):按照进程到达的顺序依次分配CPU时间片。
- 短作业优先(SJF):优先调度执行时间短的进程。
- 优先级调度:按照进程的优先级依次分配CPU时间片。
- 时间片轮转:按照一定时间间隔分配固定大小的时间片,使得每个进程都有机会执行。
进程调度算法的选择取决于具体的系统需求和性能要求。操作系统需要根据当前的系统状态和用户需求,灵活选择合适的进程调度算法,以实现最优的系统性能和用户体验。
原语
原语(primitive)是指一组可被视为单个不可分割操作的指令或函数。原语是操作系统的基本构建块,用于实现并发控制和同步操作。
原语通常具有两个特点:
- 原语是原子性的:原语是一个不可分割的操作,它要么全部执行成功,要么不执行任何操作。在多线程或多进程的环境中,原语的原子性保证了并发操作的正确性和可靠性。
- 原语是不可中断的:在原语执行期间,操作系统不会中断该操作,也不会将CPU时间片分配给其他进程或线程,以保证操作的正确性和可靠性。
原语通常用于实现并发控制和同步操作,如实现互斥锁、条件变量、信号量等。在操作系统中,原语是操作系统内核的一部分,由操作系统内核提供支持,并且只有内核态程序才能执行原语操作。
临界资源
临界资源(Critical Resource)是指同时被多个进程或线程访问的共享资源,如共享内存、IO设备等。由于多个进程或线程可以同时访问临界资源,可能会导致数据的不一致性和操作的竞争条件,因此需要进行并发控制和同步操作。
在并发编程中,为了保证对临界资源的正确访问,需要使用同步机制,如互斥锁、条件变量、信号量等。同步机制可以保证在任何时候,只有一个进程或线程可以访问临界资源,从而避免了数据不一致性和竞争条件的问题。
临界资源的访问是一个常见的并发编程问题,需要注意的是,在设计并发程序时,需要仔细考虑访问临界资源的顺序和方式,以避免出现死锁和饥饿等问题。此外,在使用同步机制时,需要根据具体的应用场景和需求,选择合适的同步机制,以保证程序的正确性和可靠性。
线程
线程(Thread)是指进程中的一个执行单元,是操作系统调度执行的最小单位。一个进程可以包含多个线程,每个线程执行不同的任务,共享进程的资源。
线程是轻量级的执行单元,相比进程,线程的创建和销毁、上下文切换等操作开销更小,可以更高效地利用计算机的资源。多线程编程可以提高程序的并发性和性能,对于需要同时执行多个任务的应用程序,使用多线程编程技术可以提高程序的吞吐量和响应速度。
线程可以分为用户线程和内核线程。用户线程是在用户空间中实现的线程,由用户程序自己管理和调度。内核线程是由操作系统内核创建和管理的线程,由操作系统内核进行调度和管理。
锁
对一间房子加锁,目的是为了组织不该进入的人;对临界资源,我们也可以通过加锁,阻止不该进入的进程。
锁的概念
锁(Lock)是一种同步机制,用于控制对共享资源的访问。在多线程编程中,由于多个线程可能同时访问共享资源,可能会出现数据不一致性和竞争条件等问题,锁可以保证在任何时刻,只有一个线程可以访问共享资源,从而避免这些问题。
锁的分类
常见的锁包括互斥锁(Mutex)、读写锁(RWLock)、自旋锁(Spinlock)等。
-
互斥锁可以保证在任何时刻,只有一个线程可以访问共享资源,其他线程需要等待互斥锁释放后才能访问。
-
读写锁可以支持多个线程同时读取共享资源,但只允许一个线程写入共享资源。
-
自旋锁是一种忙等待的锁,当共享资源被占用时,线程会一直自旋等待,直到锁被释放。
锁的使用
锁的使用应该注意:
- 锁的粒度:锁的粒度决定了锁的范围和开销。过细的锁粒度会增加锁的开销,过粗的锁粒度会影响程序的并发性和性能。
- 死锁和饥饿:在使用多个锁时,需要避免出现死锁和饥饿等问题,以保证程序的可靠性和性能。
- 锁的公平性:锁的公平性决定了锁的获取顺序和执行顺序。公平的锁会按照请求的顺序来分配锁,而非公平的锁则可能导致某些线程等待时间过长。
- 锁的性能:锁的性能直接影响程序的并发性和性能。需要根据具体的应用场景和需求,选择合适的锁,以实现高性能的程序。
进程与线程
进程和线程都是计算机中的执行单元,但它们有一些区别。
进程是一个正在执行的程序的实例。每个进程都有自己的内存空间、系统资源和状态。进程可以被看作是一个独立的程序,它可以运行在操作系统的环境中,有自己的地址空间、堆栈和数据段。进程之间是相互独立的,它们不能直接共享内存和系统资源。进程间通信需要使用一些特定的机制,如管道、消息队列、信号量等。
线程是进程中的执行单元,它是进程中的一部分,共享了进程的内存空间和系统资源。线程可以看作是轻量级进程,它比进程更加轻便和灵活。线程之间可以直接共享内存和系统资源,因此线程间通信更加方便和快捷。在多线程编程中,程序员可以将任务分解成多个线程来并发执行,提高程序的性能。
程的内存空间和系统资源。线程可以看作是轻量级进程,它比进程更加轻便和灵活。线程之间可以直接共享内存和系统资源,因此线程间通信更加方便和快捷。在多线程编程中,程序员可以将任务分解成多个线程来并发执行,提高程序的性能。
总的来说,进程和线程都是计算机中的执行单元,但它们的主要区别在于进程是独立的执行环境,而线程是共享进程环境的执行单元。线程相对于进程来说更加轻量级,创建和销毁线程的开销也比进程小。但是,线程之间的共享需要考虑同步和互斥的问题,否则会导致竞争和死锁等问题。