线程(代码实现)详解

来源:互联网 发布:梦幻西游mac右键 编辑:程序博客网 时间:2024/05/21 23:32
在计算机科学中,一个线程执行的是,可以独立地被一个管理编程指令的最小序列调度,这是通常的的一部分的操作系统。线程和所述的实施过程的操作系统之间的不同,但在大多数情况下,一个线程的过程的一个组成部分。多个线程可以在一个过程中存在,执行同时和共享的资源,例如存储器,而不同的过程不共享这些资源。特别是,一个进程的线程共享其可执行代码和它的变量在任何给定时间的值。

具有单处理器系统通常实现由多线程时间分片:所述中央处理单元(CPU)的不同之间切换的软件线程。这种上下文切换通常发生非常频繁,并迅速不够,用户感知的线程或任务并行运行。在一个多处理器或多核系统,多个线程可在执行平行,与每一个处理器或核心同时执行一个单独的线程; 处理器或核心上的硬件线程,独立的软件线程,也可以同时由单独的硬件线程执行。

版面中作出了提前出现OS / 360多道处理任务数量可变的(MVT)于1967年,其背景下,他们被称为“任务”。术语“线程”已被归因于维克多A. Vyssotsky。 处理调度许多现代操作系统的直接支持时间分片和多处理器线程,和操作系统内核允许程序员通过曝光所需的功能来操纵线程系统调用接口。一些线程实现被称为内核线程,而重量轻的过程(LWP)是共享相同的状态和信息内核线程的一个特定类型。此外,程序可以拥有用户空间线程与定时器,信号或其他方法线程中断自己的执行时,执行排序的临时时间分片。

线程与进程
线程传统不同的多任务 操作系统进程在于:
*过程通常是独立的,而线程作为一个过程的子集存在
*流程进行相当多的状态不是线程的信息,而一个过程份额的过程州内多个线程和内存等资源
*进程具有独立的地址空间,而线程共享它们的地址空间
*进程只能通过系统提供的交互的进程间通信机制
上下文切换在同一进程线程之间通常比上下文进程之间切换速度更快。
如系统的Windows NT和OS / 2据说具有廉价线程和昂贵的过程; 在其他操作系统中没有一个除外的成本如此之大的差别地址空间的开关,在一些系统上(特别是86),结果在转换后备缓冲器(TLB)平齐。


单线程
在计算机编程,单个线程是处理一个命令一次。

多线程
多线程主要存在于多任务操作系统。多线程是一个普遍的编程和执行模型,允许多个线程一个进程的上下文中存在。这些线程共享进程的资源,但能够独立执行。该线程编程模型为开发人员提供并发执行的一个有用的抽象。多线程还可以应用于一个过程,以使并行执行一个上多系统。

多线程应用程序具有以下优点
回应:多线程可以让应用程序保持响应输入。在一个线程的程序,如果在长时间运行的任务的主执行线程块,整个应用程序可以出现冻结。通过移动这样长时间运行的任务到一个工作线程与主执行线程同时运行,这是可能的,而在后台执行任务的应用程序保持响应于用户输入。另一方面,在大多数情况下,多线程是不保留一个节目响应的唯一方式,与非阻塞的I / O和/或Unix的信号是可用于获得类似的结果。
更快的执行:多线程程序的这个优点允许它更快操作的计算机系统具有多个中央处理单元(CPU)的或一个或多个多核处理器,或跨集群的机器,因为程序的螺纹自然借给自己去并行执行,假设有足够的独立性(即他们并不需要等待对方)。
较低的资源消耗:使用线程,应用程序可以为多个客户端同时使用更少的资源比使用其自身的多个副本的过程时,那就需要。例如,Apache的HTTP服务器使用线程池:监听进程收听传入的请求池和服务器的线程来处理这些请求池。
更好系统利用率:作为一个例子,使用多个线程可以实现,因为在更快的介质中的数据(如高速缓冲存储器)更高的吞吐量和较低的延迟一个文件系统可以由一个线程,而另一个线程检索从较慢介质的数据(例如检索作为外部存储)与没有线程等待另一个以完成。
简化的共享和交流:与流程,这需要一个消息传递或共享内存机制来执行进程间通信(IPC),线程可以通过数据,代码,他们已经共享文件通信。
并行化:希望利用多核或者多CPU系统可以使用多线程的数据和任务拆分成子任务并行,让底层架构的应用程序管理线程运行方式,无论是在同时一个核心或多个内核上并行。像GPU计算环境CUDA和OpenCL的使用多线程模型,其中几十到几百的线程中运行并行跨数据上大量的核心。

多线程有以下缺点
同步:由于线程共享相同的地址空间中,程序员必须小心,以避免竞争条件和其他非直观行为。为了将数据正确地操纵,线程将经常需要会合时间,以便按正确的顺序来处理数据。线程可能还需要互斥操作(通常实现使用信号量,以防止在被修改的过程中同时被修改或读取,而共同的数据)。小心使用这种原语可能会导致死锁。
主题崩溃的过程:由一个线程执行了非法操作崩溃的全过程; 因此,一个运行异常线程可以破坏在应用程序中的所有其他线程的处理。

调度
操作系统调度线程要么抢先或协作。抢先多线程通常被认为是优良的方法,因为它允许操作系统,以确定何时一个上下文切换应该发生。抢占式多线程的缺点是,该系统可以在不合适的时间使一个前后关系切换,造成锁车队,优先级反转或其他负面影响,这可以通过合作的多线程来避免。协作多线程,另一方面,依赖于线程本身放弃控制一旦它们在一个停止点。这可以创建如果一个线程等待资源变得可用的问题。

在处理器的嵌入式系统,它有更高的要求实时的行为,可能会通过降低线程切换时间,也许是通过分配一个专用的支持多线程寄存器文件中的每个线程,而不是保存/恢复通用寄存器文件。


进程,内核线程,用户线程和纤维
调度可以在内核级别或用户级别上进行,和多任务处理可以先发制人地或协作完成。这产生了各种相关的概念。

在内核级别,一个处理包含一个或多个内核线程,它们共享进程的资源,诸如存储器和文件句柄-一个过程是对资源的单位,而一个线程调度和执行的一个单位。内核调度通常均匀抢先做,或者不常用,合作。在用户级的处理,如运行时系统本身可以安排多个执行线程。如果这些不共享数据,如在二郎,他们通常是类似于称为进程,而如果它们共享数据,他们通常被称为(用户)的线程,特别是如果抢先调度。协同调度的用户线程被称为纤维 ; 不同的过程可能有不同的调度用户线程。用户线程可通过各种方式内核线程来执行(一个比一,多到酮,多到多)。所谓“ 轻量进程 ”不同地是指用户线程或内核机制调度用户线程到内核线程。

一个进程是内核调度的“重量级”单位,创建,销毁和切换过程相对昂贵。进程拥有资源由操作系统分配的。资源包括存储器(代码和数据),文件句柄,插座,设备句柄,窗口和一个过程控制块。过程中分离由工艺隔离,和不共享地址空间或文件资源,除非通过明确的方法,如继承文件句柄或共享存储器段,或映射相同的文件中的一个共享的方式-见间通信。创建或销毁过程是相对昂贵的,因为资源必须获得或释放。过程通常是抢先多任务,并且过程切换是相对昂贵,超出基本成本上下文切换,由于诸如缓存刷新的问题。

一个内核线程的内核调度的“轻量级”单位。的至少一个内核线程每个过程中存在。如果多个内核线程可以在一个过程中存在,则它们共享相同的存储器和文件资源。如果操作系统的过程中内核线程被抢先式多任务调度是先发制人。内核线程不拥有资源,除了一个栈,该副本的寄存器包括程序计数器,以及线程本地存储(如果有的话),并因此相对便宜的创建和销毁。线程切换也相对便宜的:它需要一个历境切换(保存和恢复寄存器和栈指针),但不改变虚拟存储器,因此高速缓冲存储器型(离去TLB无效)。内核可以在一个系统中分配一个线程每个逻辑核心(因为每个处理器分割本身成多个逻辑核心是否支持多线程,或者仅支持每个如果没有物理芯一个逻辑核心),并可换出线程被封锁。然而,内核线程需要比要交换用户线程长得多。

线程都在执行,有时用户空间库,就称为用户线程。内核是不知道他们,使他们的管理和计划在用户空间。一些实现立足于几个内核线程顶部的用户线程,从中受益的多处理器机器(M:N模式)。在这篇文章中,术语“线程”(没有内核或用户预选赛)默认为参照内核线程。通过实施为用户线程的虚拟机也被称为绿色线程。用户线程一般都是快速创建和管理,但不能利用多线程或者多了,如果他们所有的相关的内核线程被阻塞,即使有一些用户线程准备运行将得到阻止。

纤维是调度的更轻的单元,它们合作计划:一个正在运行的纤维必须明确“ 收益 ”,让另一根光纤来运行,这使得他们实现更容易比内核或用户线程。一种纤维可以预定在同一过程中的任何线程中运行。这允许应用程序通过管理调度自己,而不是依靠内核调度(可能不进行调整应用程序),以获得性能的提升。并行编程环境中,如OpenMP的通常是通过光纤实现他们的任务。密切相关的纤维是协同程序,与区别在于协程是一个语言级构建体,而纤维是一个系统级的结构。


并发和数据结构
在同一进程线程共享相同的地址空间。这允许同时运行的代码,情侣紧紧地和没有的开销和复杂性方便地进行数据交换IPC。当线程之间共享,但是,即使是简单的数据结构变得容易发生竞争条件两个线程最终可能试图更新的同时的数据结构,并发现它意外改变脚下:如果他们需要一个以上的CPU的指令进行更新。引起竞争条件错误可以是非常难以复制和隔离。

为了防止这种情况,线程应用程序编程接口(API)的报价同步原语,如互斥,以锁定对并发访问的数据结构。在单处理器系统,运行到锁定的互斥线程必须睡觉,因此引发上下文切换。在多处理器系统中,线程可代替轮询一个互斥自旋锁。这两种可能削弱性能和力处理器的对称多处理(SMP)系统,以争用存储器总线,特别是如果粒度锁定的是好的。

I / O和调度
网友跟帖或光纤实现通常是完全在用户空间。其结果是,上下文同一进程内的用户线程或纤维之间的切换是非常有效的,因为它不需要与内核在所有的任何相互作用:一个上下文切换可通过在本地保存由当前正在执行用户线程所使用的CPU寄存器执行或纤维,然后装载用户线程或光纤所需的寄存器被执行。由于调度在用户空间时,调度政策可更容易地适合于该程序的工作负荷的要求。


然而,使用阻断系统调用在用户线程(相对于内核线程)或纤维可以是有问题的。如果一个用户线程或纤维执行系统调用,在进程块,其他用户线程和纤维无法直到系统调用返回运行。这个问题的一个典型的例子是执行I / O时:大多数程序被写入同步执行I / O。当启动的I / O操作,系统调用时,并不会返回,直到I / O操作完成。在此期间,整个过程被“封锁”由内核和不能运行,这饿死其他用户线程和纤维在同一进程的执行。


这个问题的一个常见的​​解决方案是提供实现通过使用非阻塞I /内部O和调度另一个用户线程或纤维,而I / O操作正在进行同步接口的I / O API。类似的解决方案可以提供用于其它阻挡系统调用。另外,该程序可以写入避免使用同步I / O或其他拦截系统调用。

使用的内核线程通过移动一些穿入内核最为复杂的问题简化了用户的代码。程序不需要调度线程或明确地放弃处理器。用户代码可以写在一个熟悉的程序风格,包括呼叫阻塞的API,而不挨饿其他线程。然而,内核线程可以随时强制线程之间的上下文切换,从而暴露种族危险,并发错误,否则将位于潜。在SMP系统中,这进一步加剧,因为内核线程可以在不同的处理器上并行执行字面上。

实用的多线程
为线程执行的标准化接口是POSIX线程(Pthreads中),这是一组C函数库的调用。操作系统供应商可以自由,以根据需要实现的接口,但应用程序开发人员应当能够使用多个平台相同的接口。大多数的Unix平台,包括Linux支持Pthreads中。微软Windows有自己的一套线程函数在process.h接口的多线程,就像beginthread。Java提供了使用主机操作系统的又一标准化接口的Java并发库的java.util.concurrent。


多线程库提供一个函数调用来创建一个新的线程,这需要一个函数作为参数。同时一个线程,然后创建它开始运行传递的功能,当函数返回结束。该线程库还提供同步功能,这使其能够实现竞争条件 -错误使用免费的多线程功能互斥,条件变量的关键部分,信号量,显示器和其他同步原语。


线程使用的另一种模式是,线程池,其中在启动时然后等待分配任务创建线程的定数。当一个新的任务到来时,它唤醒,完成任务并回到等待。这避免了相对昂贵的线程创建和销毁功能执行的每个任务和需要线程管理出应用程序开发者的手并离开到库或操作系统,更适合优化线程管理。例如,框架,如大中央调度和线程构建模块。


在编程模型如CUDA专为数据并行计算,线阵列运行相同的代码只使用其ID找到其内存中的数据并行。在本质上,应用程序必须被设计成使得每个线程上执行的存储器不同的段相同的动作,使他们能够在并行操作和使用的GPU架构。


接下来,我们就来看看怎么创建一个进程,结束进程和线程的等待

首先,创建进程:

我们先来了解一个函数:


在来写一个程序,这个程序是创建一个线程,使其输出HHHHHHH,而主程序输出hello


再来看看结果:


看来,我们实现了上面的所说的功能(也就是创建一个线程,使其输出一段字符);

那我们要如何结束它呢?

这样可以么?


看看结果


确实是结束了,那把return换成exit呢?

或者,把程序变成这样




结果发现,进程退出了。




那么,最后,我们来看看,如何等待一个进程


接下来,我们用一个代码来实现:


看看结果



本博文理论部分查考维基百科,代码实现部分只是简单的实现了线程的创建,终止,和等待,至于其他的互斥与同步问题,将在以后详细介绍,谢谢



0 0
原创粉丝点击