秒杀多线程学习笔记

来源:互联网 发布:故宫淘宝是故宫开的吗 编辑:程序博客网 时间:2024/05/16 15:44
原文地址:http://blog.csdn.net/morewindows/article/details/7392749






    线程的基本概念,线程的基本状态及状态之间的转换
线程,有时称为轻量级进程,是CPU使用的基本单位;它由线程ID、程序计数器、寄存器集合、和堆栈组成。它与属于同一进程的其他线程共享其代码段、数据段和其他操作系统资源。
线程有五种状态:新生状态,运行状态,就绪状态,阻塞状态,死亡状态






    线程和进程的区别
1.线程是进程的一部分,所以线程有时候被称为轻权进程或者轻量级进程。
2.系统在运行的时候会为每个进程分配不同的内存区域,但是不会为线程分配内存,线程只能它所属进程的资源。
3.与进程的控制表PCB类似,线程也有自己的控制表TCB,但是TCB中所保存的线程状态比PCB表中少多了。
4.进程是操作系统所有资源分配的基本单位,拥有一个完整的虚拟空间地址,并不依赖线程而独立存在。
而线程是操作系统执行任务的基本单位。只占有少量必须的资源,必须存在于进程中。






    多线程同步和互斥有几种实现方法,都是什么
线程间同步方法大体分为两类:用户模式和内核模式。顾名思义,内核模式就是指利用系统内核对象来进程同步,使用时需要切换内核态与用户态,而用户模式就是不需要切换到内核态,只在用户态完成操作。
用户模式下方法有:原子操作,临界区。内核模式下的方法有:事件,信号量,互斥量。






    多线程同步和互斥有何异同,在什么情况下分别使用他们?
线程同步是指线程之间所具有的一种制约关系,一个线程的执行依赖另一个线程的消息,当它没有得到另一个线程的消息时应等待,知道消息到达时才被唤醒。
线程互斥是指对于共享的进程系统资源,在各单个线程访问时的排它性。当有若干个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用,其他要使用该资源的线程必须等待,知道占用资源者释放该资源。线程互斥可以看成是一种特殊的线程同步。






    相交线程之间的关系主要有两种,同步和互斥。
同步和互斥的关系
同步是指线程之间运行必须严格按照某种特定的先后次序来运行,这种先后次序依赖于要完成的特定的任务。
互斥是指当某个线程运行到其中一个临界资源时,其他线程就不能得到这个临界资源,必须等待这个进入临界资源的线程退出之后才可以进入。






    创建线程的方法。
_beginthreadex 和 CreateThread  
创建线程的两个函数的区别
CreateThread和_beginthreadex的区别就是:_beginthreadex()函数在创建新线程时会分配并初始化一个_tiddata块,这个块用来存放一些需要线程独享的数据




解决线程同步的方法






原子锁(原子操作)解决多线程中共享变量的问题
多线程环境中对一个变量进行读写时,需要有一种方法能够保证对一个值得改变是原子操作-即不可打断性,一个线程在执行原子操作时,其他线程必须等待它完成之后才开始执行该原子操作。


关键段(临界区)解决多线程同步互斥问题
其中LPCRITICAL_SECTION是一个结构体,里面有六个参数,其中一个参数表示拥有该关键段的句柄,还有一个是旋转锁的设置。
通过这个结构体可以知道关键段会记录拥有该关键段的线程句柄即关键段是有“线程所有权”概念的,只有当拥有该关键段的线程调用LeaveCriticalSection时,才会使其进入的次数为0时,系统会自动更新关键段并将等待中的线程换回可调度状态。关键段可以用于线程间的互斥,不可以用于同步。
由于将线程切换到等待状态的开销较大,为了提高关键段的性能,将旋转锁合并到关键段中,这样在EnterCriticalSection会先用一个旋转锁不断循环,尝试一段时间后才会将线程切换到等待状态。如果主机只有一个处理器(CPU)那么设置旋转锁是无效的,无法进入关键区域的线程总会被系统将其切换到等待状态。
关键段(临界区)固有特点(优点+缺点): 
1、是一个用户模式的对象,不是系统核心对象;所以跨进程使用。 
2、因为不是核心对象,所以执行速度快,有效率; 
3、可以多次“进入”,但必须多次“退出”; 
4、最好不要同时进入或等待多个 Critical Sections,容易造成死锁; 
5、无法检测到进入到 Critical Sections 里面的线程当前是否已经退出!(无法解决遗弃问题)






关键段与原子锁的区别。
临界区是为了确保同一个代码片段在同一时间只能被一个线程访问,与原子锁不同的是临界区是多条指令的锁定,而原子锁仅仅对单条操作指令有效;临界区和原子锁只能控制同一个进程中线程的同步.






用事件来解决多线程同步问题
事件是内核对象,所以可以跨进程室友。事件分为手动置为事件和自动置为事件。事件内部包含一个使用计数(所有内核对象都有),一个布尔值表示是手动置为事件还是自动置为事件,还有一个布尔值表示事件有无触发。事件可以被设置为触发,也可以被设置为非触发。事件用来解决线程间同步的问题,因此也可以解决线程间互斥的问题






用互斥量解决多线程同步问题
互斥量也是一个内核对象,他用来确保一个线程独占一个资源的访问,互斥量和关键段非常相似。两者的区别在于互斥量可以用于不同进程的线程互斥访问资源。互斥量只能用于线程互斥,不能用于线程同步。互斥量会记录线程ID。并且能完美解决某个进程意外退出而造成的“遗弃”问题。






用信号量解决多线程同步问题
当前资源数量大于0,表示信号量处于触发,等于0表示资源耗尽,信号量没触发。线程可以在信号量不为0时进入临界资源。






线程(进程)同步的主要任务
答:在引入多线程后,由于线程执行的异步性,会给系统造成混乱,特使是在急用临界资源时,可能会导致数据处理出错。因此线程同步的主要任务是使并发执行的各线程之间能够有效的共享资源和相互合作,从而使程序的执行具有可在现性。






线程(进程)之间的制约关系
当线程并发执行时,由于资源共享和线程协作,使用线程之间会存在以下两种制约关系。
(1)间接相互制约。一个系统中的多个线程必然要共享某种系统资源。如共享CPU,共享IO设备。所谓间接制约即源于这种资源共享,即有一个线程在使用这种资源时,其他线程都需要等待。
(2)直接相互制约。这种制约主要是因为线程之间的合作,如一个线程将结果提供给另一个线程处理,那么另一个线程在这个线程将数据送达之前都处于阻塞状态。
间接制约可以称为互斥,直接制约称为同步。对于互斥可以这样理解,线程A和线程B互斥访问某个资源则他们之间机会产生顺序问题——要么线程A等待线程B操作完毕,要么线程B等待线程A操作完毕,这其实就是线程的同步了。因此同步包括互斥,互斥其实是一种特殊的同步。






临界资源和临界区
在一段时间内只允许一个线程访问的资源就称为临界资源或独占资源,计算机中大多数物理设备,进程中的共享变量等待都是临界资源,他们要求被互斥的访问。每个进程中访问临界资源的代码称为临界区。






解决多线程同步的几种方法
原子锁,关键段,事件,互斥量,信号量。其中后三种都是内核对象,可以跨进程使用。








上述中涉及的函数
CreateThread
  HANDLE WINAPI CreateThread(LPSECURITY_ATTRBUTE lpThreadAttrbutes//安全属性
SIZE_T dwStackSize//线程栈大小
LPTHREAD_START_ROUTINE lpStartAddress//线程函数地址
LPVOID lpParameter//线程传递的参数
DWORD dwCreationFlags//线程创建后运行还是挂起
LPDWORD lpThreadID);//线程ID


WaitForSingleObject
  HANDLE WINAPI WaitForSingleObject (HANDLE hHandle//要等待的内核对象
,DWORD dwMilliseconds);//最长等待时间


有关原子锁的函数
LONG __cdecl InterlockedIncrement(LONG volatile *Addend) 对变量加一的原子操作
LONG __cdecl InterlockedDecrement(LONG volatile *Addend) 对变量减一的原子操作
LONG __cdecl InterlockedExchangeAdd(LONG volatile *Addend,LONG Value)对变量加多少的原子操作
LONG __cdecl InterlockedExchange(LONG volatite *Target,LONG Value) 对变量赋值的原子操作


有关关键段的函数
void InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection)初始化关键段
void DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection)销毁关键段
void EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection)进入关键段
void LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection)离开关键段
BOOL TryEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection)尝试进入关键段
BOOL InitializeCriticalSectionAndSpinCount(LPCRITICAL_SECTION lpCriticalSection DWORD dwSpinCount)     初始化关键段并设置旋转次数 
DWORD SetCriticalSectionSpinCount(LPCRITICAL_SECTION lpCriticalSection DWORD dwSpinCount)修改关键段的旋转次数


有关事件的函数
HANDLE CreateEvent(LPSECURITY_ATTRIBUTES lpEventAttributes,BOOL bManualReset, LPCTSTR lpName)
HANDLE OpenEvent(DWORD dwDesiredAccess,BOOL bInheritHandle,LPCTSTR lpName)
BOOL  SetEvent(HANDLE hEvent)
BOOL ResetEvent(HANDLE hEvent)
由于事件是内核对象,所以调用CloseHandle就可以完成清理和销毁了。


有关互斥量的函数
HANDLE CreateMutex(LPSECRITY_ATTRIBUTE lpMutexAttributes,BOOL bInitialOwner,LPCTSTR lpName)
HANDLE OpenMutex(DOWRD dwDesiredAccess,BOOL bInitialHandle,LPCTSTR lpName)
BOOL ReleaseMutex(HANDLE hMutex)
由于互斥量是内核对象,所有调用CloseHandle就可以完成清理和销毁了。


有关信号量的函数
HANDLE CreateSemaphore(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,LONG lInitialCount,LONG lMaximumCount,LPCTSTR lpName);
HANDLE OpenSemaphore(DWORD dwDesiredAccess,BOOL bInheritHandle,LPCTSTR lpName);
BOOL ReleaseSemaphore(HANDLE hSemaphore,LONG lReleaseCount,  LPLONG lpPreviousCount );
由于信号量是内核对象,因此使用CloseHandle就可以完成清理和销毁

























0 0
原创粉丝点击