ImageVerifierCode 换一换
格式:DOCX , 页数:22 ,大小:128.99KB ,
资源ID:5096800      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/5096800.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(操作系统.docx)为本站会员(b****5)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

操作系统.docx

1、操作系统线程第一课时计算机系统由硬件和软件两部分组成,操作系统OS(Operating System)是配置在计算机硬件上的第一层软件,是对硬件的首次扩充。操作系统的定义:是控制和管理计算机系统的硬件和软件资源,合理地组织计算机工作流程,以及方便用户的程序和数据的集合。见下图:当前windows 操作系统 属于实时操作系统,也是抢占式操作系统。它有这样几个特性:并发、共享、虚拟、异步。操作系统的主要任务,是为多道程序的运行提供良好的运行环境,以保证多道程序有条不紊地、高效的运行,并能最大程度地提高系统中各种资源的利用率和方便用户的使用。在传统的操作系统中,程序并不能独立运行,作为资源分配和独立

2、运行的基本单位是进程。如果说,在操作系统中,引入进程的目的,是为了是多个程序能并发执行,以提高资源利用率和系统吞吐量,那么,在操作系统中,引入线程,则是为了减少程序在并发执行时所付出的开销。线程的定义:线程(thread, 台湾称 执行绪)是进程中某个单一顺序的控制流。也被称为轻量进程(lightweight processes)。计算机科学术语,指运行中的程序的调度单位。线程两部分组成:一个是线程的内核对象 操作系统用它管理线程。系统还用内核对象来存放线程统计信息的地方。一个线程栈 用于维护线程执行时所需的所有函数参数和局部变量。1)轻型实体线程中的实体基本上不拥有系统资源,只是有一点必不可

3、少的、能保证独立运行的资源,比如,在每个线程中都应具有一个用于控制线程运行的线程控制块TCB,用于指示被执行指令序列的程序计数器、保留局部变量、少数状态参数和返回地址等的一组寄存器和堆栈。2)独立调度和分派的基本单位。在多线程OS中,线程是能独立运行的基本单位,因而也是独立调度和分派的基本单位。由于线程很“轻”,故线程的切换非常迅速且开销小。3)可并发执行。在一个进程中的多个线程之间,可以并发执行,甚至允许在一个进程中所有线程都能并发执行;同样,不同进程中的线程也能并发执行。4)共享进程资源。在同一进程中的各个线程,都可以共享该进程所拥有的资源,这首先表现在:所有线程都具有相同的地址空间(进程

4、的地址空间),这意味着,线程可以访问该地址空间的每一个虚地址;此外,还可以访问进程所拥有的已打开文件、定时器、信号量机构等。线程与进程的区别可以归纳为以下几点:1)地址空间和其它资源(如打开文件):进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。2)通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信需要进程同步和互斥手段的辅助,以保证数据的一致性。3)调度和切换:线程上下文切换比进程上下文切换要快得多。4)在多线程OS中,进程不是一个可执行的实体。并行是指在同一时刻,有多条指令在多个处理器上同时执行。并发是指在同一时刻,只能有一条指令执行,但多

5、个进程指令被快速轮换执行,使得在宏观上具有多个进程同时执行的效果。“并行”是指无论从微观还是宏观,二者都是一起执行的,就好像两个人各拿一把铁锨在挖坑,一小时后,每人一个大坑。 而“并发”在微观上不是同时执行的,只是把时间分成若干段,使多个进程快速交替的执行,从宏观外来看,好像是这些进程都在执行,这就好像两个人用同一把铁锨,轮流挖坑,一小时后,两个人各挖一个小一点的坑,要想挖两个大一点得坑,一定会用两个小时。 从以上本质不难看出,“并发”执行,在多个进程存在资源冲突时,并没有从根本提高执行效率。何时创建线程?比如公交车,一个司机开一圈,两个司机开一圈 ,时间可能两个司机更慢一些。多此一举。接下来

6、我们来由一个例子引入线程:在一个按钮的事件处理中:while(im_bflag*/) Sleep(20); p-m_progressCtrl.SetPos(i+); 当程序在走while循环时,程序死等在那里,只有while循环结束,才能继续执行其他代码。因此引入线程.一 、首先,创建线程:Win32 提供了一系列的API函数来完成线程的创建、挂起、恢复、终结以及通信等工作。下面将选取其中的一些重要函数进行说明。 1、HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, DWORD dwStackSize, LPTHREAD

7、_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId);该函数在其调用进程的进程空间里创建一个新的线程,并返回已建线程的句柄,其中各参数说明如下:lpThreadAttributes:指向一个 SECURITY_ATTRIBUTES 结构的指针,该结构决定了线程的安全属性,一般置为 NULL; dwStackSize:指定了线程的堆栈深度,一般都设置为0; lpStartAddress:表示新线程开始执行时代码所在函数的地址,即线程的起始地址。一般情况为(LPTH

8、READ_START_ROUTINE)ThreadFunc,ThreadFunc 是线程函数名; lpParameter:指定了线程执行时传送给线程的32位参数,即线程函数的参数; dwCreationFlags:控制线程创建的附加标志,可以取两种值。如果该参数为0,线程在被创建后就会立即开始执行;如果该参数为CREATE_SUSPENDED,则系统产生线程后,该线程处于挂起状态,并不马上执行,直至函数ResumeThread被调用; lpThreadId:该参数返回所创建线程的ID; 如果创建成功则返回线程的句柄,否则返回NULL。 2、DWORD SuspendThread(HANDLE

9、hThread);该函数用于挂起指定的线程,如果函数执行成功,则线程的执行被终止。 3、DWORD ResumeThread(HANDLE hThread);该函数用于结束线程的挂起状态,执行线程。4、VOID ExitThread(DWORD dwExitCode);该函数用于线程终结自身的执行,主要在线程的执行函数中被调用。其中参数dwExitCode用来设置线程的退出码。5、BOOL TerminateThread(HANDLE hThread,DWORD dwExitCode);一般情况下,线程运行结束之后,线程函数正常返回,但是应用程序可以调用TerminateThread强行终止某

10、一线程的执行。各参数含义如下:hThread:将被终结的线程的句柄; dwExitCode:用于指定线程的退出码。 使用TerminateThread()终止某个线程的执行是不安全的,可能会引起系统不稳定;虽然该函数立即终止线程的执行,但并不释放线程所占用的资源。因此,一般不建议使用该函数。 6、BOOL PostThreadMessage(DWORD idThread, UINT Msg, WPARAM wParam, LPARAM lParam);该函数将一条消息放入到指定线程的消息队列中,并且不等到消息被该线程处理时便返回。idThread:将接收消息的线程的ID; Msg:指定用来发送

11、的消息; wParam:同消息有关的字参数; lParam:同消息有关的长参数; 调用该函数时,如果即将接收消息的线程没有创建消息循环,则该函数执行失败。二、写个例子:类似播放器的按钮,开始 、暂停 和停止1点击开始按钮创建线程 m_hThread = CreateThread(NULL,/是否被继承 0, /线程堆栈的大小 &ThreadProc, /函数的地址 this, /线程函数参数 CREATE_SUSPENDED ,/创建标志0立即执行 &m_nid /线程ID ); if (!m_hThread) MessageBox(_T(线程创建失败); ResumeThread(m_hTh

12、read);/恢复线程创建线程函数DWORD WINAPI CthreadDlg:ThreadProc(LPVOID lpParameter) CthreadDlg *p = (CthreadDlg*)lpParameter; while (1/*p-m_bflag*/) /如果事件有信号则结束当前线程 if (WaitForSingleObject(p-m_hEvent,100) = WAIT_OBJECT_0) break; int i = 0; while(im_bflag*/) Sleep(20); p-m_progressCtrl.SetPos(i+); /有信号 SetEvent(

13、p-m_hCheckEvent); return 0;2.点击暂停 挂起线程SuspendThread(m_hThread);3点击停止 -结束线程结束线程时有3种方法(1) 全局变量(2) 事件(双事件)将一个事件置为有信号,则线程中有个等待函数,当等到信号,则跳出循环,线程结束。(3) 强制终止Exitthread() 结束线程,只能结束当前线程,将操作系统分配的资源回收Terminatethread() 结束任意线程,不会释放该线程资源,直到包含当前线程的进程结束。用户界面线程1课时1. 用MFC 的 afxBeginthread创建线程 有两种工作者线程 :后台运行,没有自己的消息队列

14、,直到向这个线程中发送消息,系统会为此线程创建一个消息队列.用户界面线程:有自己的界面和消息队列。创建用户界面线程的步骤:1.添加一个对话框类2.添加一个又CWinThread派生的一个线程类在线程的初始化函数InitInstance中 Cdig dig; m_pMainwnd = &dig; /将对话框和线程类联系在一块,作为用户界面线程的主窗口.Dig.domodal();问题: 1.如果先关闭自己创建出来的线程,最后关闭进程的主线程没问题。如果直接关闭主线程,则会有内存泄露,为什么呢?原因是由于包含自己创建的线程的进程已经结束,自己线程强制终止,造成内存泄露。解决方法:在主线程退出之前,

15、将自己创建的线程一一结束。(通过发送消息的方式)。代码如下: /结束自己创建出来的线程 for (int i = 0;i PostThreadMessage(WM_QUIT,0,0); WaitForSingleObject( m_pthreadi-m_hThread,INFINITE); 线程间通信1课时线程间通信有两种方法: 1. 全局变量2. 发送消息由一个例子说明:题目:首先点击启动线程 创建一个线程A,然后去选择一个要去运算的数,点击计算的时候,将这个数 由主线程-线程A ,线程A计算出结果,将结果由线程A-主线程1. 启动线程m_hThread = CreateThread(NUL

16、L,0,&ThreadProc,this,CREATE_SUSPENDED,&m_nThreadID); if (!m_hThread) MessageBox(_T(创建线程失败); ResumeThread(m_hThread);DWORD WINAPI Cthread2Dlg:ThreadProc(LPVOID lpParameter) Cthread2Dlg *pthis = (Cthread2Dlg*)lpParameter; MSG msg; while (1) int sum = 0; if (PeekMessage(&msg,NULL,0,0,PM_REMOVE) if (msg

17、.message = UM_MSG) for (int i = 0; i m_nsum*/;i+) sum += i; /将计算结果返回到主线程 / pthis-PostMessage(UM_MSG,sum); / theApp.PostThreadMessage(UM_MSG,sum,0); /* CString str; str.Format(_T(%d),sum); pthis-GetDlgItem(IDC_EDIT1)-SetWindowText(str);*/ return 0;2. 计算 /获得当前点击的单选按钮(即要计算的值) UpdateData(TRUE); switch (

18、m_nradio) case 0: m_nsum = 100; break; case 1: m_nsum = 1000; break; case 2: m_nsum = 10000; break; /将要计算的结果发送给线程A PostThreadMessage(m_nThreadID,UM_MSG,m_nsum,0);注意:如果是通过/ theApp.PostThreadMessage(UM_MSG,sum,0); 将数据由线程A 发向UI线程,则应该在APP类中,添加响应线程的自定义消息代码如下:void onmsg(WPARAM wparam,LPARAM lparam);ON_THR

19、EAD_MESSAGE(UM_MSG,&Cthread2App:onmsg)void Cthread2App:onmsg(WPARAM wparam,LPARAM lparam) CString str; str.Format(_T(%d),wparam); /GetDlgItem(IDC_EDIT1)-SetWindowText(str); 线程同步2课时虽然多线程能给我们带来好处,但是也有不少问题需要解决。例如,对于像磁盘驱动器这样独占性系统资源,由于线程可以执行进程的任何代码段,且线程的运行是由系统调度自动完成的,具有一定的不确定性,因此就有可能出现两个线程同时对磁盘驱动器进行操作,从而

20、出现操作错误;又例如,对于银行系统的计算机来说,可能使用一个线程来更新其用户数据库,而用另外一个线程来读取数据库以响应储户的需要,极有可能读数据库的线程读取的是未完全更新的数据库,因为可能在读的时候只有一部分数据被更新过。使隶属于同一进程的各线程协调一致地工作称为线程的同步。MFC提供了多种同步对象,下面只介绍最常用的四种: 临界区(CCriticalSection) 事件(CEvent) 互斥量(CMutex) 信号量(CSemaphore)通过这些类,可以比较容易地做到线程同步。 由一个卖火车票例子引入: 现在由xx 地到xx地 有1000张车票,火车站有10个窗口同时售票,直到卖完为止。

21、解题思路:首先,创建10个线程,同时执行卖票操作,线程在操作系统中并行或者并发的运行,不能保证同一张票就卖给一个人,或者 可能卖到负数的情况。所以引入临界区和互斥量。0 .考虑到多个线程访问一个变量的问题。可以采用原子访问。原子访问:指的是一个线程在访问某个资源的同时,能够保证没有其他线程会在同一时刻访问同一资源。Interlocked系列。InterlockedExchangeAdd(&g_x,1);InterlockedIncrement(&g_x);InterlockedDecrement(&g_x);Interlocked 系列函数非常好用。我们当然优先考虑使用它们。但是大多数实际的编

22、程问题需要处理的数据结构往往比一个简单的32位值 或者 64位值复杂的多。这个时候需要用到关键段 即临界区。关键段:是一小段代码,它在执行之前需要独占对一些共享资源的访问权。这种方式可以让多行代码以“原子方式”来对资源进行操控。这里的原子方式,指的是代码知道除了当前线程外,没有其他任何线程会同时访问该资源。当前系统仍然可以暂停当前线程去调度其他的线程,但是在当前线程离开关键段之前,系统是不会调度任何想要访问同一资源的其他线程。CRITICAL_SECTION g_cs; /比如 飞机上的卫生间 ,马桶则是我们要保存的数据IniticalizeCriticalsection(&g_cs); /初

23、始化CRITICAL_SECTION 中的成员变量EnterCriticalSection(&g_cs); /线程用它来检查占用标志的函数/有如下几种情况,(1)如果当前没有线程在访问资源(卫生间门上显示为无人),那么它将允许调用线程进入“卫生间”。将门上的标志置为有人.(2)如果EnterCriticalSection发现已经有另一个线程在“卫生间”,那么调用线程在“卫生间”门外等待(系统会为第一想要访问该“卫生间”的线程创建一个事件的内核对象,将其由用户模式切换到内核模式,处于等待状态),直到另一个线程离开“卫生间”为止。(系统会从一些想要访问“卫生间” 的线程中,将其中一个线程置为可调度

24、状态)LeaveCriticalSection(&g_cs); /离开“卫生间”,门上重新置为无人DeleteCriticalsection(&g_cs); /删除事件的内核对象,以及为CRITICAL_SECTION初始化的资源。注意:如果现在有线程正在访问临界区,其他想要访问临界区的线程走到EnterCriticalSection 的时候,EnterCriticalSection会把此线程由用户模式切换到内核模式(切换的时间大约是1000个CPU周期),当访问临界区的线程离开临界区,系统会将想要访问临界区的线程切换到可调度的状态。 切换过于浪费时间,(1)所以可以用TryEnterCrit

25、icalSection 来代替EnterCriticalSection。TryEnterCriticalSection线程在访问时,如果不能访问资源,那么它继续做其他事情,而不用等待。(2)用旋转锁。为了提高关键段的性能,microsoft把旋转锁合并到了关键段中 .因此,当调用EnterCriticalSection的时候,它会用一个旋转锁不断的循环,尝试在一段时间内获得访问权。只有当尝试失败的时候,线程才会切换到内核模式并进入等待状态。为了在使用关键段的时候同时使用旋转锁,我们必须调用下面的函数来初始化关键段。bool InitializeCriticalSectionAndSpinCou

26、nt( PCRITICAL_SECTION pcs, DOWD dwSpinCount);SetCriticalSectionSpinCount( /改变旋转锁的次数PCRITICAL_SECTION pcs, DOWD dwSpinCount)关键段和错误处理一使用 CCriticalSection 类 当多个线程访问一个独占性共享资源时,可以使用“临界区”对象。任一时刻只有一个线程可以拥有临界区对象,拥有临界区的线程可以访问被保护起来的资源或代码段,其他希望进入临界区的线程将被挂起等待,直到拥有临界区的线程放弃临界区时为止,这样就保证了不会在同一时刻出现多个线程访问共享资源。CCritic

27、alSection类的用法非常简单,步骤如下:1. 定义CCriticalSection类的一个全局对象(以使各个线程均能访问),如CCriticalSection critical_section; 2. 在访问需要保护的资源或代码之前,调用CCriticalSection类的成员Lock()获得临界区对象: critical_section.Lock();3. 在线程中调用该函数来使线程获得它所请求的临界区。如果此时没有其它线程占有临界区对象,则调用Lock()的线程获得临界区;否则,线程将被挂起,并放入到一个系统队列中等待,直到当前拥有临界区的线程释放了临界区时为止。 4. 访问临界区完

28、毕后,使用CCriticalSection的成员函数Unlock()来释放临界区:critical_section.Unlock();通俗讲,就是线程A执行到critical_section.Lock();语句时,如果其它线程(B)正在执行critical_section.Lock();语句后且critical_section. Unlock();语句前的语句时,线程A就会等待,直到线程B执行完critical_section. Unlock();语句,线程A才会继续执行。 二使用互斥量CMUTEX类互斥量内核对象用来确保一个线程独占对于一个资源的访问。互斥量的用法相当于 现在有一堆人,等待空

29、中的气球(互斥量),一旦有人抢到了,则他独占,直到他释放互斥量。互斥对象包含一个使用计数、线程ID以及一个递归计数。线程ID:用来标识当前占用这个互斥量的是系统中那个线程。如果为false 则此互斥量是触发状态,否则,当前线程有对互斥量的拥有权。直到他释放,其他线程才可以拥有。递归计数:表示这个线程占用该互斥量的次数。互斥量CMUTEX类的用法非常简单,步骤如下:1. 创建互斥量HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes, /安全属性 BOOL bInitialOwner, /如果为false 则此互斥量是触发状态,否

30、则,当前线程有对互斥量的拥有权。 LPCTSTR lpName /互斥量的名字);2. 在线程函数中抢互斥量的拥有权,一旦拥有,则其他线程不可以再去抢夺WaitForSingleObject(pthis-m_mutex,INFINITE);直到此线程调用ReleaseMutex(pthis-m_mutex);其他线程可以继承,抢夺对互斥量的拥有权。互斥对象与临界区对象很像.互斥对象与临界区对象的不同在于:1. 互斥对象可以在进程间使用,而临界区对象只能在同一进程的各线程间使用。当然,互斥对象也可以用于同一进程的各个线程间,但是在这种情况下,使用临界区会更节省系统资源,更有效率。2. 关键段(临界区对象) 是用户模式下的同步对象,所以效率会高一些。互斥量是内核对象,内核对象的唯一缺点就是它们的性能。调用线程必须从用户模式切换到内核模式。这种切换是非常耗时的:在X86平台上,一个空的系统调用大概会占用200个CPU 周期当然,这还不包括执行被调用函数在内核

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1