四种线程同步(或互斥)方式小结

一,什么是线程同步和互斥
同步就是协同步调,按预定的先后次序进行运行。如:你说完,我再说。这里的同步千万不要理解成那个同时进行,应是指协同、协助、互相配合。线程同步是指多线程通过特定的设置(如互斥量,事件对象,临界区)来控制线程之间的执行顺序(即所谓的同步)也可以说是在线程之间通过同步建立起执行顺序的关系,如果没有同步,那线程之间是各自运行各自的!

线程互斥是指对于共享的进程系统资源,在各单个线程访问时的排它性。当有若干个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用,其它要使用该资源的线程必须等待,直到占用资源者释放该资源。线程互斥可以看成是一种特殊的线程同步(下文统称为同步)。


文末收录了一篇关于并发 并行 同步 异步 多线程的区别(这是原文地址,尊重原创)

二,线程同步的方式和机制
临界区(Critical Section)、互斥对象(Mutex):主要用于互斥控制;都具有拥有权的控制方法,只有拥有该对象的线程才能执行任务,所以拥有,执行完任务后一定要释放该对象。

信号量(Semaphore)、事件对象(Event):事件对象是以通知的方式进行控制,主要用于同步控制!

1、临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。在任意时刻只允许一个线程对共享资源进行访问,如果有多个线程试图访问公共资源,那么在有一个线程进入后,其他试图访问公共资源的线程将被挂起,并一直等到进入临界区的线程离开,临界区在被释放后,其他线程才可以抢占。它并不是核心对象,不是属于操作系统维护的,而是属于进程维护的。

总结下关键段:
1)关键段共初始化化、销毁、进入和离开关键区域四个函数。
2)关键段可以解决线程的互斥问题,但因为具有“线程所有权”,所以无法解决同步问题。
3)推荐关键段与旋转锁配合使用。

2、互斥对象:互斥对象和临界区很像,采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以能保证公共资源不会同时被多个线程同时访问。当前拥有互斥对象的线程处理完任务后必须将线程交出,以便其他线程访问该资源。

总结下互斥量Mutex:
1)互斥量是内核对象,它与关键段都有“线程所有权”所以不能用于线程的同步。
2)互斥量能够用于多个进程之间线程互斥问题,并且能完美的解决某进程意外终止所造成的“遗弃”问题。


3、信号量:信号量也是内核对象。它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目

在用CreateSemaphore()创建信号量时即要同时指出允许的最大资源计数和当前可用资源计数。一般是将当前可用资源计数设置为最 大资源计数,每增加一个线程对共享资源的访问,当前可用资源计数就会减1 ,只要当前可用资源计数是大于0 的,就可以发出信号量信号。但是当前可用计数减小 到0 时则说明当前占用资源的线程数已经达到了所允许的最大数目,不能在允许其他线程的进入,此时的信号量信号将无法发出。线程在处理完共享资源后,应在离 开的同时通过ReleaseSemaphore ()函数将当前可用资源计数加1 。在任何时候当前可用资源计数决不可能大于最大资源计数。

4、事件对象: 通过通知操作的方式来保持线程的同步,还可以方便实现对多个线程的优先级比较的操作

总结下事件Event
1)事件是内核对象,事件分为手动置位事件和自动置位事件。事件Event内部它包含一个使用计数(所有内核对象都有),一个布尔值表示是手动置位事件还是自动置位事件,另一个布尔值用来表示事件有无触发。
2)事件可以由SetEvent()来触发,由ResetEvent()来设成未触发。还可以由PulseEvent()来发出一个事件脉冲。
3)事件可以解决线程间同步问题,因此也能解决互斥问题。

三,四种线程同步控制方式
1,互斥对象
第一个CreateMutex
函数功能:创建互斥量(注意与事件Event的创建函数对比)
函数原型:
HANDLE  CreateMutex(
LPSECURITY_ATTRIBUTES  lpMutexAttributes,
BOOL  bInitialOwner, 
LPCTSTR  lpName
);
函数说明:
第一个参数表示安全控制,一般直接传入NULL。
第二个参数用来确定互斥量的初始拥有者。如果传入TRUE表示互斥量对象内部会记录创建它的线程的线程ID号并将递归计数设置为1,由于该线程ID非零,所以互斥量处于未触发状态。如果传入FALSE,那么互斥量对象内部的线程ID号将设置为NULL,递归计数设置为0,这意味互斥量不为任何线程占用,处于触发状态。
第三个参数用来设置互斥量的名称,在多个进程中的线程就是通过名称来确保它们访问的是同一个互斥量。
函数访问值:
成功返回一个表示互斥量的句柄,失败返回NULL。


第二个打开互斥量
函数原型:
HANDLE  OpenMutex(
DWORD   dwDesiredAccess,
BOOL  bInheritHandle,
LPCTSTR  lpName //名称
);

函数说明:
第一个参数表示访问权限,对互斥量一般传入MUTEX_ALL_ACCESS。详细解释可以查看MSDN文档。
第二个参数表示互斥量句柄继承性,一般传入TRUE即可。
第三个参数表示名称。某一个进程中的线程创建互斥量后,其它进程中的线程就可以通过这个函数来找到这个互斥量。
函数访问值:
成功返回一个表示互斥量的句柄,失败返回NULL。


第三个触发互斥量
函数原型:
BOOL  ReleaseMutex (HANDLE  hMutex)
函数说明:
访问互斥资源前应该要调用等待函数,结束访问时就要调用ReleaseMutex()来表示自己已经结束访问,其它线程可以开始访问了。


最后一个清理互斥量
由于互斥量是内核对象,因此使用CloseHandle()就可以(这一点所有内核对象都一样)。

互斥对象的形象比喻:
首先我们需要创建CreateMutex一把互斥对象,我们可以指明当前线程是否拥有它,互斥对象完全就像一把钥匙一样,我们用WaitForSignalObject来等待这把钥匙,但是这把钥匙被等到并且使用后必须释放-----ReleaseMutex ,不然别人永远无法等到。这样从等待到释放中间的代码段永远都是只有一个线程在执行,也就形成了互斥控制。当然互斥对象的句柄是要关闭的CloseHandle。

结合下面的程序理解简直轻松。

互斥对象模拟火车售票的过程
(对共同资源的互斥控制)

#include "windows.h"
#include "iostream"
 
using namespace std;
 
DWORD WINAPI FunProc1(LPVOID lpParameter);
DWORD WINAPI FunProc2(LPVOID lpParameter);
 
int ticket = 100;
HANDLE hMutex;   //定义互斥对象
int main()
{
    HANDLE hThread1;
    HANDLE hThread2;
    hMutex = CreateMutex(NULL, FALSE, NULL);    //创建互斥对象,并且该线程不拥有他(因为第二个参数为false)
    hThread1 = CreateThread(NULL, 0, FunProc1, NULL, 0, NULL);
    hThread2 = CreateThread(NULL, 0, FunProc2, NULL, 0, NULL);
    CloseHandle(hThread1);
    CloseHandle(hThread2);
    Sleep(1000);// 让主线程睡眠1秒 ,不然子线程来不及执行
    return 0;
}
 
DWORD WINAPI FunProc1(LPVOID lpParameter)
{
    while (TRUE)
    {
        WaitForSingleObject(hMutex, INFINITE);   //申请互斥对象的所有权(申请钥匙,得到钥匙)
        if (ticket>0)
        {
            Sleep(1);
            cout << "ticket 1:" << ticket-- << endl;
        }
        else
            break;
        ReleaseMutex(hMutex);//释放互斥对象的所有权(放弃钥匙,不再拥有)
    }
    return 0;
}
DWORD WINAPI FunProc2(LPVOID lpParameter)
{
    while (TRUE)
    {
        WaitForSingleObject(hMutex, INFINITE);
        if (ticket>0)
        {
            Sleep(1);
            cout << "ticket 2:" << ticket-- << endl;
        }
        else
            break;
        ReleaseMutex(hMutex);
    }
    return 0;
}


分析程序互斥过程
创建互斥对象后,第二各参数是false表示当前主线程不拥有互斥对象,此时互斥对象没人拥有,所以谁都可以获取他,然后主线程空闲1秒(子线程才时间执行,不然主线程一下就退出了),在这一秒内第一个线程waitforsingleobject得到了互斥对象(不然无法执行),开始卖火车票,然后释放互斥对象的拥有权,接着第二个线程通过waitforsingleobject得到了互斥对象,所以他又可以执行售卖火车票,接着释放互斥对象...如此往返!知道火车票卖完,while循环退出。
注意:虽然改程序运行结果是某一条线程执行完然后第二条线程执行,如此往复,但是这不是同步,因为我们没法控制到底一开始是谁先执行,(我们只是控制了轮流次序)。


2,事件对象
第一个 CreateEvent
函数功能:创建事件
函数原型:
HANDLE  CreateEvent(
LPSECURITY_ATTRIBUTES   lpEventAttributes,
BOOL   bManualReset,
BOOL   bInitialState,
LPCTSTR  lpName
);
函数说明:
第一个参数表示安全控制,一般直接传入NULL。
第二个参数确定事件是手动置位还是自动置位,传入TRUE表示手动置位,传入FALSE表示自动置位。如果为自动置位,则对该事件调用WaitForSingleObject()后会自动调用ResetEvent()使事件变成未触发状态。
第三个参数表示事件的初始状态,传入TRUR表示已触发。
第四个参数表示事件的名称,传入NULL表示匿名事件。

第二个 OpenEvent
函数功能:根据名称获得一个事件句柄。
函数原型:
HANDLE  OpenEvent(
DWORD  dwDesiredAccess,
BOOL  bInheritHandle,
LPCTSTR  lpName //名称
);
函数说明:
第一个参数表示访问权限,对事件一般传入EVENT_ALL_ACCESS。详细解释可以查看MSDN文档。
第二个参数表示事件句柄继承性,一般传入TRUE即可。
第三个参数表示名称,不同进程中的各线程可以通过名称来确保它们访问同一个事件。


第三个SetEvent
函数功能:触发事件
函数原型:BOOL   SetEvent(HANDLE  hEvent);
函数说明:每次触发后,必有一个或多个处于等待状态下的线程变成可调度状态。


第四个ResetEvent
函数功能:将事件设为末触发
函数原型:BOOLResetEvent(HANDLEhEvent);


最后一个事件的清理与销毁
由于事件是内核对象,因此使用CloseHandle()就可以完成清理与销毁了。


事件对象的形象比喻:
首先我们需要创建CreateEvent一个事件对象,它的使用方式是触发方式,要想被WaitForSingleObject等待到该事件对象必须是有信号的,事件要想有信号可以用SetEvent手动置为有信号,要想事件对象无信号可以使用ResetEvent(或者在创建事件对象时就声明该事件对象WaitForSingleObject后自动置为无信号,见上面CreateEvent第二个参数),打个小小比方,手动置位事件相当于教室门,教室门一旦打开(被触发),所以有人都可以进入直到老师去关上教室门(事件变成未触发)。自动置位事件就相当于医院里拍X光的房间门,门打开后只能进入一个人,这个人进去后会将门关上,其它人不能进入除非门重新被打开(事件重新被触发)。当然事件对象的句柄是要关闭的CloseHandle。

结合下面的程序理解简直轻松。


事件对象模拟火车售票的过程
(利用通知的方式对共同资源的互斥控制)

#include "windows.h"
#include "iostream"
 
using namespace std;
 
DWORD WINAPI FunProc1(LPVOID lpParameter);
DWORD WINAPI FunProc2(LPVOID lpParameter);
int ticket = 100;
HANDLE g_hEvent;
void main()
{
    HANDLE hThread1;
    HANDLE hThread2;
    g_hEvent = CreateEvent(NULL, FALSE, TRUE, NULL);      //创建事件对象
    //SetEvent(g_hEvent);           //设置为有信号(放弃钥匙)
    hThread1 = CreateThread(NULL, 0, FunProc1, NULL, 0, NULL);
    hThread2 = CreateThread(NULL, 0, FunProc2, NULL, 0, NULL);
    CloseHandle(hThread1);
    CloseHandle(hThread2);
    Sleep(1000);             // 让主线程睡眠1秒 
    CloseHandle(g_hEvent);
}
DWORD WINAPI FunProc1(LPVOID lpParameter)
{
    while (TRUE)
    {
        WaitForSingleObject(g_hEvent, INFINITE);     //申请事件对象
        //ResetEvent(g_hEvent);       //重置为无信号(申请钥匙,得带钥匙)
        if (ticket>0)
        {
            Sleep(1);
            cout << "ticket 1:" << ticket-- << endl;
            SetEvent(g_hEvent);     //设置为有信号(放弃钥匙,不再拥有)
        }
        else
        {
            SetEvent(g_hEvent);      //设置为有信号
            break;
        }
    }
    return 0;
}
DWORD WINAPI FunProc2(LPVOID lpParameter)
{
    while (TRUE)
    {
        WaitForSingleObject(g_hEvent, INFINITE);
        //ResetEvent(g_hEvent);
        if (ticket>0)
        {
            Sleep(1);
            cout << "ticket 2:" << ticket-- << endl;
            SetEvent(g_hEvent);       //设置为有信号
        }
        else
        {
            SetEvent(g_hEvent);       //设置为有信号
            break;
        }
    }
    return 0;
}


同步过程分析
g_hEvent=CreateEvent(NULL,FALSE,TRUE,NULL);的意义(顺序解释):默认安全性,自动重置事件,初始时该事件对象就有信号。执行顺序:开始的时候事件对象具有信号,当第一个线程申请获得事件对象后,进入if语句线程1会暂停1毫秒,于是第二根线程运行,因为此时g_hEvent已经无信号故无法申请并执行下面的程序,此时第一个线程睡醒开始执行自己的任务然后设置对象为有信号(可以被其他线程申请),于是第二个线程申请得到事件对象................与此往复!直到退出循环。

注意:该程序实现的不是的同步控制!但是他可以实现同步,见下面!

下面这种事件通知方式就是严格的同步方式(先让线程1执行,再.....),两个事件对象进行同步控制:

#include "windows.h"  
#include "iostream"  
 
using namespace std;
 
DWORD WINAPI FunProc1(LPVOID lpParameter);
DWORD WINAPI FunProc2(LPVOID lpParameter);
int ticket = 100;
HANDLE g_hEvent1,g_hEvent2;
 
void main()
{
    HANDLE hThread1;
    HANDLE hThread2;
    g_hEvent1 = CreateEvent(NULL, FALSE, TRUE, NULL);      //创建事件对象1,自动重置事件,已触发
    g_hEvent2 = CreateEvent(NULL, FALSE, FALSE, NULL);      //创建事件对象2,自动重置事件,没触发
    //SetEvent(g_hEvent);           //设置为有信号(放弃钥匙)  
    hThread1 = CreateThread(NULL, 0, FunProc1, NULL, 0, NULL);
    hThread2 = CreateThread(NULL, 0, FunProc2, NULL, 0, NULL);
    CloseHandle(hThread1);
    CloseHandle(hThread2);
    Sleep(1000);             // 让主线程睡眠1秒   
    CloseHandle(g_hEvent1);
    CloseHandle(g_hEvent2);
    getchar();
}
DWORD WINAPI FunProc1(LPVOID lpParameter)
{
    while (TRUE)
    {
        WaitForSingleObject(g_hEvent1, INFINITE);     //申请有信号的事件对象  
        //ResetEvent(g_hEvent);       //重置为无信号(申请钥匙,得带钥匙)  
        if (ticket>0)
        {
            Sleep(1);
            cout << "ticket 1:" << ticket-- << endl;
            SetEvent(g_hEvent2);     //设置为有信号(放弃钥匙,不再拥有)  
        }
        else
        {
            SetEvent(g_hEvent2);      //设置为有信号,让线程2执行
            break;
        }
    }
    return 0;
}
DWORD WINAPI FunProc2(LPVOID lpParameter)
{
    while (TRUE)
    {
        WaitForSingleObject(g_hEvent2, INFINITE);
        //ResetEvent(g_hEvent);  
        if (ticket>0)
        {
            Sleep(1);
            cout << "ticket 2:" << ticket-- << endl;
            SetEvent(g_hEvent1);  //让线程1执行
        }
        else
        {
            SetEvent(g_hEvent1);       
            break;
        }
    }
    return 0;
}


3,临界区(关键代码段)
函数功能:初始化
函数原型:
void InitializeCriticalSection(LPCRITICAL_SECTION  lpCriticalSection);
函数说明:定义关键段变量后必须先初始化。


函数功能:销毁
函数原型:
void DeleteCriticalSection(LPCRITICAL_SECTION  lpCriticalSection);
函数说明:用完之后记得销毁。


函数功能:进入关键区域
函数原型:
void EnterCriticalSection(LPCRITICAL_SECTION  lpCriticalSection);
函数说明:系统保证各线程互斥的进入关键区域。


函数功能:离开关关键区域
函数原型:
void LeaveCriticalSection(LPCRITICAL_SECTION  lpCriticalSection);


临界区模拟火车售票的过程
添加代码如下

#include "windows.h"
#include "iostream"
 
using namespace std;
 
DWORD WINAPI FunProc1(LPVOID lpParameter);
DWORD WINAPI FunProc2(LPVOID lpParameter);
int ticket = 100;
CRITICAL_SECTION g_cs;     //定义临界区
void main()
{
    HANDLE hThread1;
    HANDLE hThread2;
    InitializeCriticalSection(&g_cs);    //必须先初始化临界区
    hThread1 = CreateThread(NULL, 0, FunProc1, NULL, 0, NULL);
    hThread2 = CreateThread(NULL, 0, FunProc2, NULL, 0, NULL);
    CloseHandle(hThread1);
    CloseHandle(hThread2);
    Sleep(1000);// 让主线程睡眠1秒 
    DeleteCriticalSection(&g_cs);     //删除临界区
}
DWORD WINAPI FunProc1(LPVOID lpParameter)
{
    while (TRUE)
    {
        EnterCriticalSection(&g_cs);//进入临界区(申请钥匙,得到钥匙)
        Sleep(1);
        if (ticket>0)
        {
            Sleep(1);
            cout << "ticket 1:" << ticket-- << endl;
            LeaveCriticalSection(&g_cs);   //离开(放弃钥匙,不再拥有)
        }
        else
        {
            LeaveCriticalSection(&g_cs);    //离开
            break;
        }
    }
    return 0;
}
DWORD WINAPI FunProc2(LPVOID lpParameter)
{
    while (TRUE)
    {
        EnterCriticalSection(&g_cs);    
        if (ticket>0)
        {
            Sleep(1);
            cout << "ticket 2:" << ticket-- << endl;
            LeaveCriticalSection(&g_cs);   
        }
        else
        {
            LeaveCriticalSection(&g_cs);  
            break;
        }
    }
    return 0;
}


4,信号量
信号量Semaphore常用有三个函数,使用很方便。下面是这几个函数的原型和使用说明。
HANDLE   CreateSemaphore( 
LPSECURITY ATTRIBUTES   lpSemaphoreAttributes, //安全属性 
LONG   lInitialCount, //信号量对象的初始值 
LONG   lMaximumCount, //信号量的最大值 
LPCTSTR   lpName //信号量名 
); 
参数说明: 
(1)lpSemaphoreAttributes:指定安全属性,为NULL时,信号量得到一个 
默认的安全描述符。 
(2) lInitialCount:指定信号量对象的初始值。该值必须大于等于0,小于等于lMaximumCount。当其值大于0时,信号量被唤醒。当该函数释放了一个等待该信号量的线程时,lInitialCount值减1,当调用函数ReleaseSemaphore()时,按其指定的数量加一个值。
(3) lMaximumCount:指出该信号量的最大值,该值必须大于0。 
(4) lpName:给出信号量的名字。 
返回值: 
信号量创建成功,将返回该信号量的句柄。如果给出的信号量名是系统已经存在的信号量,将返回这个已存在信号量的句柄。如果失败,系统返回NULL,可以调用函数GetLastError()查询失败的原因

第二个 OpenSemaphore
函数功能:打开信号量
函数原型:
HANDLE  OpenSemaphore(
DWORD  dwDesiredAccess,
BOOL  bInheritHandle,
LPCTSTR  lpName
);

函数说明:
第一个参数表示访问权限,对一般传入SEMAPHORE_ALL_ACCESS。详细解释可以查看MSDN文档。
第二个参数表示信号量句柄继承性,一般传入TRUE即可。
第三个参数表示名称,不同进程中的各线程可以通过名称来确保它们访问同一个信号量。


第三个 ReleaseSemaphore
函数功能:递增信号量的当前资源计数
函数原型:
BOOL  ReleaseSemaphore(
HANDLE  hSemaphore,
LONG  lReleaseCount, 
LPLONG  lpPreviousCount 
);
函数说明:
第一个参数是信号量的句柄。
第二个参数表示增加个数,必须大于0且不超过最大资源数量。
第三个参数可以用来传出先前的资源计数,设为NULL表示不需要传出。

注意:当前资源数量大于0,表示信号量处于触发,等于0表示资源已经耗尽故信号量处于末触发。在对信号量调用等待函数时,等待函数会检查信号量的当前资源计数,如果大于0(即信号量处于触发状态),减1后返回让调用线程继续执行。一个线程可以多次调用等待函数来减小信号量。

最后一个 信号量的清理与销毁
由于信号量是内核对象,因此使用CloseHandle()就可以完成清理与销毁了。

信号量超形象解释:
以一个停车场的运作为例。简单起见,假设停车场只有三个车位(共有资源),一开始三个车位都是空的。这时如果同时来了五辆车(线程),看门人(信号量)允许其中三辆(线程)直接进入,然后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口处等待。这时,有一辆车(线程)离开停车场,看门人(信号量)得知后,打开车拦,放入外面的一辆进去,如果又离开两辆,则又可以放入两辆,如此往复。
抽象的来讲,信号量的特性如下:信号量是一个非负整数(车位数),所有通过它的线程/进程(车辆)都会将该整数减一(通过它使得资源被使用了1个),当该整数值为零时,所有试图通过它的线程(车辆)都将处于等待状态。在信号量上我们定义两种操作: Wait(等待函数) 和 Release(释放函数)。当一个线程调用Wait操作时,它要么得到资源然后将信号量减一,要么一直等下去(指放入阻塞队列),直到信号量大于等于一时。Release(释放)对应于车辆离开停车场,该操作之所以叫做“释放”是因为释放了由信号量守护的资源(车位)。


参考学习例子:

#include <stdio.h>
#include <process.h>
#include <windows.h>
 
long g_nNum;
unsigned int __stdcall Fun(void *pPM);
const int THREAD_NUM = 10;
//信号量与关键段
HANDLE            g_hThreadParameter;//用信号量处理主线程与子线程的同步
CRITICAL_SECTION  g_csThreadCode;//关键段来处理各子线程间的互斥.对公有资源进行互斥访问,公有资源g_nNum
 
int main()
{
    //初始化信号量和关键段
    g_hThreadParameter = CreateSemaphore(NULL, 0, 1, NULL);//第二个参数表示初始资源数量。当前0个资源,最大允许1个同时访问
    InitializeCriticalSection(&g_csThreadCode);
 
    HANDLE  handle[THREAD_NUM];
    g_nNum = 0;
    int i = 0;
    while (i < THREAD_NUM)
    {
        handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);
        Sleep(1);
        WaitForSingleObject(g_hThreadParameter, INFINITE);//依次使线程进入等待状态(阻塞队列)。参见《windows核心编程第五版》
        ++i;
    }
 
    WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
 
    DeleteCriticalSection(&g_csThreadCode);
    //销毁信号量和关键段
    CloseHandle(g_hThreadParameter);
    for (i = 0; i < THREAD_NUM; i++)
        CloseHandle(handle[i]);
    
    getchar();
    return 0;
}
unsigned int __stdcall Fun(void *pPM)
{
    int nThreadNum = *(int *)pPM;
    ReleaseSemaphore(g_hThreadParameter, 1, NULL);//信号量的当前资源+1,第二个参数表示增加个数,必须大于0且不超过最大资源数量。
    Sleep(50);//some work should to do
    EnterCriticalSection(&g_csThreadCode);
    ++g_nNum;
    Sleep(10);//some work should to do
    printf("线程编号为%d  全局资源值为%d\n", nThreadNum, g_nNum);
    LeaveCriticalSection(&g_csThreadCode);
    return 0;
}


收录博文:

1. 并发:在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行。其中两种并发关系分别是同步和互斥


2. 互斥:进程间相互排斥的使用临界资源的现象,就叫互斥。


3. 同步:进程之间的关系不是相互排斥临界资源的关系,而是相互依赖的关系。进一步的说明:就是前一个进程的输出作为后一个进程的输入,当第一个进程没有输出时第二个进程必须等待。具有同步关系的一组并发进程相互发送的信息称为消息或事件。其中并发又有伪并发和真并发,伪并发是指单核处理器的并发,真并发是指多核处理器的并发。


4. 并行:在单处理器中多道程序设计系统中,进程被交替执行,表现出一种并发的外部特种;在多处理器系统中,进程不仅可以交替执行,而且可以重叠执行。在多处理器上的程序才可实现并行处理。从而可知,并行是针对多处理器而言的。并行是同时发生的多个并发事件,具有并发的含义,但并发不一定并行,也亦是说并发事件之间不一定要同一时刻发生。

5. 多线程:多线程是程序设计的逻辑层概念,它是进程中并发运行的一段代码。多线程可以实现线程间的切换执行。

6. 异步:异步和同步是相对的,同步就是顺序执行,执行完一个再执行下一个,需要等待、协调运行。异步就是彼此独立,在等待某事件的过程中继续做自己的事,不需要等待这一事件完成后再工作。线程就是实现异步的一个方式。异步是让调用方法的主线程不需要同步等待另一线程的完成,从而可以让主线程干其它的事情。
异步和多线程并不是一个同等关系,异步是最终目的,多线程只是我们实现异步的一种手段。异步是当一个调用请求发送给被调用者,而调用者不用等待其结果的返回而可以做其它的事情。实现异步可以采用多线程技术或则交给另外的进程来处理。


为了对以上概念的更好理解举一个简单例子, 

 假设我要做 烧开水,举杠铃100下, 洗衣服 3件事情。

烧开水 这件事情, 我要做的事情为, 准备烧开水 1分钟, 等开水烧开 8 分钟 , 关掉烧水机 1分钟
举杠铃100下 我要做的事情为, 举杠铃100下 10分钟
洗衣服 我要做的事情为, 准备洗衣服 1分钟, 等开水烧开 5 分钟 , 关掉洗衣机 1分钟

单核情况下
同步的完成,我需要做的时间为 1+ 8 +1 + 10 + 1+ 5 +1 = 27 分

如果异步,就是在等的时候,我可以切换去做别的事情

准备烧开水(1) + 准备洗衣服(1) + 举50下杠铃 (5)分钟+ 关洗衣机 1分钟 + 举杠铃20下 (2)分钟+ 关烧水机 1分钟 + 举30下杠铃(3)分钟
1+1+5+1+2+1+3 =14 分钟


双核 异步 并行

核1 准备烧开水 1分钟+ 举杠铃50下(5)分钟+ 等待3分钟 + 关掉烧水机 1分钟

核2 准备洗衣服 1分钟+ 举杠铃50下(5)分钟+ 关掉洗衣机 1分钟 + 等待3分钟

其实只花了 1+5+3+1 = 10分钟

其中还有双核都等待了3分钟
双核 异步 非并行

核1 举杠铃100下(10)分钟

核2 准备烧开水 1分钟+ 准备洗衣服 1分钟+ 等待5 分钟+ + 关掉烧水机 1分钟 + 等待 1 分钟 + 关掉洗衣机 1分钟

其实只花了 1+5+3+1 = 10分钟
多线程的做法
单核下

线程1 准备烧开水 1分钟, 等开水烧开 8 分钟 , 关掉烧水机 1分钟
线程2 举杠铃100下 10分钟
线程3 准备洗衣服 1分钟, 等开水烧开 5 分钟 , 关掉洗衣机 1分钟

cpu 可能这么切换 最理想的切换方式

线程1 准备烧开水1 sleep 1 sleep 5 sleep 1 sleep 2 关开水 1分钟 exit
线程2 sleep 1 sleep 1 举杠铃50 5分钟 sleep 1 举杠铃20 2分钟 sleep1 举杠铃30下 3分钟 
线程3 sleep 1 准备洗衣服1 分钟 sleep 5 关洗衣机1分钟 exit

最后使用了 14分钟 和异步是一样的。
但是实际上是不一样的,因为线程不会按照我们设想的去跑, 如果线程2 举杠铃先跑,整个流程的速度就下来了。

异步和同步的区别, 在io等待的时候,同步不会切走,浪费了时间。

如果都是独占cpu 的业务, 比如举杠铃的业务, 在单核情况下 多线和单线 没有区别。

多线程的好处,比较容易的实现了 异步切换的思想, 因为异步的程序很难写的。多线程本身程还是以同步完成,但是应该说
比效率是比不上异步的。 而且多线很容易写, 相对效率也高。

多核的好处,就是可以同时做事情, 这个和单核完全不一样的。

参考资源:

【1】MoreWindows的秒杀多线程系列,博客地址,http://blog.csdn.net/morewindows?viewmode=contents

【2】孙鑫《VC++深入详解》

【3】http://blog.csdn.net/cqkxboy168/article/details/9026205/

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目录 1 多线程基础介绍15 定义多线程术语15 符合多线程标准16 多线程的益处17 提高应用程序的响应 17 有效使用多处理器17 改进程序结构17 占用较少的系统资源17 结合线程和RPC(远程过程调用)18 多线程概念18 并发性和并行性 18 多线程结构一览 18 线程调度19 线程取消19 线同步20 使用64 位体系结构20 2 基本线程编程23 线程库................................................................................................................................................. 23 创建缺省线程.............................................................................................................................. 23 等待线程终止.............................................................................................................................. 25 简单线程的示例......................................................................................................................... 26 分离线程..................................................................................................................................... 28 为线程特定数据创建键............................................................................................................ 29 删除线程特定数据键................................................................................................................. 30 3 设置线程特定数据..................................................................................................................... 31 获取线程特定数据..................................................................................................................... 32 获取线程标识符......................................................................................................................... 36 比较线程ID ................................................................................................................................ 36 初始化线程................................................................................................................................. 37 停止执行线程.............................................................................................................................. 38 设置线程的优先级..................................................................................................................... 38 获取线程的优先级..................................................................................................................... 39 向线程发送信号......................................................................................................................... 40 访问调用线程的信号掩码........................................................................................................ 41 安全地Fork ................................................................................................................................. 42 终止线程..................................................................................................................................... 42 结束.............................................................................................................................................. 43 取消线程..................................................................................................................................... 43 取消线程..................................................................................................................................... 45 启用或禁用取消功能................................................................................................................. 45 设置取消类型.............................................................................................................................. 46 创建取消点................................................................................................................................. 47 将处理程序推送到栈上............................................................................................................ 48 从栈中弹出处理程序................................................................................................................. 48 3 线程属性..............................................................................................................................................51 属性对象............................................................................................................................................. 51 初始化属性................................................................................................................................. 52 销毁属性..................................................................................................................................... 53 设置分离状态.............................................................................................................................. 54 获取分离状态.............................................................................................................................. 55 设置栈溢出保护区大小............................................................................................................ 56 获取栈溢出保护区大小............................................................................................................ 57 设置范围..................................................................................................................................... 58 获取范围..................................................................................................................................... 59 设置线程并行级别..................................................................................................................... 60 获取线程并行级别..................................................................................................................... 60 设置调度策略.............................................................................................................................. 61 获取调度策略.............................................................................................................................. 62 目录 4 多线程编程指南• 2006 年10 月 设置继承的调度策略................................................................................................................. 63 获取继承的调度策略................................................................................................................. 64 设置调度参数.............................................................................................................................. 64 获取调度参数.............................................................................................................................. 65 设置栈大小................................................................................................................................. 67 获取栈大小................................................................................................................................. 68 关于栈.......................................................................................................................................... 69 设置栈地址和大小..................................................................................................................... 71 获取栈地址和大小..................................................................................................................... 73 4 用同步对象编程.................................................................................................................................75 互斥锁属性......................................................................................................................................... 76 初始化互斥锁属性对象............................................................................................................ 77 销毁互斥锁属性对象................................................................................................................. 78 设置互斥锁的范围..................................................................................................................... 79 获取互斥锁的范围..................................................................................................................... 80 设置互斥锁类型的属性............................................................................................................ 80 获取互斥锁的类型属性............................................................................................................ 82 设置互斥锁属性的协议............................................................................................................ 82 获取互斥锁属性的协议............................................................................................................ 84 设置互斥锁属性的优先级上限............................................................................................... 85 获取互斥锁属性的优先级上限............................................................................................... 86 设置互斥锁的优先级上限........................................................................................................ 87 获取互斥锁的优先级上限........................................................................................................ 88 设置互斥锁的强健属性............................................................................................................ 89 获取互斥锁的强健属性............................................................................................................ 90 使用互斥锁......................................................................................................................................... 91 初始化互斥锁.............................................................................................................................. 92 使互斥保持一致......................................................................................................................... 93 锁定互斥锁................................................................................................................................. 94 解除锁定互斥锁......................................................................................................................... 96 使用非阻塞互斥锁锁定............................................................................................................ 97 销毁互斥锁................................................................................................................................. 98 互斥锁定的代码示例................................................................................................................. 99 条件变量属性.................................................................................................................................. 105 目录 5 初始化条件变量属性............................................................................................................... 106 删除条件变量属性................................................................................................................... 106 设置条件变量的范围............................................................................................................... 107 获取条件变量的范围............................................................................................................... 108 使用条件变量.................................................................................................................................. 109 初始化条件变量....................................................................................................................... 109 基于条件变量阻塞................................................................................................................... 111 解除阻塞一个线程................................................................................................................... 112 在指定的时间之前阻塞.......................................................................................................... 114 在指定的时间间隔内阻塞...................................................................................................... 116 解除阻塞所有线程................................................................................................................... 117 销毁条件变量状态................................................................................................................... 119 唤醒丢失问题............................................................................................................................ 120 生成方和使用者问题............................................................................................................... 120 使用信号进行同步.......................................................................................................................... 124 命名信号和未命名信号.......................................................................................................... 125 计数信号量概述....................................................................................................................... 125 初始化信号............................................................................................................................... 126 增加信号................................................................................................................................... 128 基于信号计数进行阻塞.......................................................................................................... 129 减小信号计数............................................................................................................................ 129 销毁信号状态............................................................................................................................ 130 使用信号时的生成方和使用者问题.................................................................................... 131 读写锁属性....................................................................................................................................... 133 初始化读写锁属性................................................................................................................... 134 销毁读写锁属性....................................................................................................................... 134 设置读写锁属性....................................................................................................................... 135 获取读写锁属性....................................................................................................................... 136 使用读写锁....................................................................................................................................... 136 初始化读写锁............................................................................................................................ 137 获取读写锁中的读锁............................................................................................................... 138 读取非阻塞读写锁中的锁...................................................................................................... 139 写入读写锁中的锁................................................................................................................... 139 写入非阻塞读写锁中的锁...................................................................................................... 140 解除锁定读写锁....................................................................................................................... 140 销毁读写锁............................................................................................................................... 141 目录 6 多线程编程指南• 2006 年10 月 跨进程边界同步............................................................................................................................... 142 生成方和使用者问题示例...................................................................................................... 142 比较元语........................................................................................................................................... 145 5 使用Solaris 软件编程......................................................................................................................147 进程创建中的fork 问题................................................................................................................. 147 Fork-One 模型........................................................................................................................... 148 Fork-all 模型.............................................................................................................................. 151 选择正确的Fork ....................................................................................................................... 151 进程创建:exec 和exit 问题....................................................................................................... 152 计时器、报警与剖析...................................................................................................................... 152 每LWP POSIX 计时器............................................................................................................. 152 每线程报警............................................................................................................................... 153 剖析多线程程序....................................................................................................................... 153 非本地转向:setjmp 和longjmp .................................................................................................. 154 资源限制........................................................................................................................................... 154 LWP 和调度类................................................................................................................................. 154 分时调度................................................................................................................................... 155 实时调度................................................................................................................................... 155 公平共享调度程序................................................................................................................... 155 固定优先级调度....................................................................................................................... 156 扩展传统信号.................................................................................................................................. 156 同步信号................................................................................................................................... 157 异步信号................................................................................................................................... 157 延续语义................................................................................................................................... 157 对信号执行的操作................................................................................................................... 158 定向于线程的信号................................................................................................................... 160 完成语义................................................................................................................................... 162 信号处理程序和异步信号安全............................................................................................. 163 中断对条件变量的等待.......................................................................................................... 165 I/O 问题............................................................................................................................................. 166 I/O 作为远程过程调用............................................................................................................ 167 人为的异步性............................................................................................................................ 167 异步I/O ..................................................................................................................................... 167 共享的I/O 和新的I/O 系统调用.......................................................................................... 169 目录 7 getc 和putc 的替代项............................................................................................................. 169 6 安全和不安全的接口...................................................................................................................... 171 线程安全........................................................................................................................................... 171 MT接口安全级别............................................................................................................................ 173 不安全接口的可重复执行函数............................................................................................. 174 异步信号安全函数.......................................................................................................................... 175 库的MT安全级别........................................................................................................................... 175 不安全库................................................................................................................................... 176 7 编译和调试....................................................................................................................................... 177 编译多线程应用程序...................................................................................................................... 177 为编译做准备............................................................................................................................ 177 选择Solaris 语义或POSIX 语义............................................................................................ 178 包括<thread.h> 或<pthread.h> .......................................................................................... 178 定义_REENTRANT 或_POSIX_C_SOURCE .................................................................................. 179 使用libthread 或libpthread 链接..................................................................................... 179 与POSIX 信号的-lrt 链接.................................................................................................... 181 将原有模块与新模块链接...................................................................................................... 181 备用线程库....................................................................................................................................... 181 调试多线程程序............................................................................................................................... 181 多线程程序中常见的疏忽性问题......................................................................................... 181 使用TNF 实用程序跟踪和调试............................................................................................ 182 使用truss ................................................................................................................................. 182 使用mdb ..................................................................................................................................... 182 使用dbx ..................................................................................................................................... 183 8 Solaris 线程编程...............................................................................................................................185 比较Solaris 线程和POSIX 线程的API ....................................................................................... 185 API 的主要差异........................................................................................................................ 185 函数比较表............................................................................................................................... 186 Solaris 线程的独有函数.................................................................................................................. 189 暂停执行线程............................................................................................................................ 190 继续执行暂停的线程............................................................................................................... 191 目录 8 多线程编程指南• 2006 年10 月 相似的同步函数-读写锁............................................................................................................. 192 初始化读写锁............................................................................................................................ 192 获取读锁................................................................................................................................... 194 尝试获取读锁............................................................................................................................ 194 获取写锁................................................................................................................................... 195 尝试获取写锁............................................................................................................................ 196 解除锁定读写锁....................................................................................................................... 196 销毁读写锁的状态................................................................................................................... 197 相似的Solaris 线程函数................................................................................................................. 199 创建线程................................................................................................................................... 199 获取最小栈大小....................................................................................................................... 201 获取线程标识符....................................................................................................................... 202 停止执行线程............................................................................................................................ 202 向线程发送信号....................................................................................................................... 203 访问调用线程的信号掩码...................................................................................................... 203 终止线程................................................................................................................................... 204 等待线程终止............................................................................................................................ 204 创建线程特定的数据键.......................................................................................................... 206 设置线程特定的数据值.......................................................................................................... 207 获取线程特定的数据值.......................................................................................................... 208 设置线程的优先级................................................................................................................... 208 获取线程的优先级................................................................................................................... 210 相似的同步函数-互斥锁............................................................................................................. 210 初始化互斥锁............................................................................................................................ 210 销毁互斥锁............................................................................................................................... 213 获取互斥锁............................................................................................................................... 213 释放互斥锁............................................................................................................................... 214 尝试获取互斥锁....................................................................................................................... 214 相似的同步函数:条件变量......................................................................................................... 215 初始化条件变量....................................................................................................................... 215 销毁条件变量............................................................................................................................ 216 等待条件................................................................................................................................... 217 等待绝对时间............................................................................................................................ 218 等待时间间隔............................................................................................................................ 218 解除阻塞一个线程................................................................................................................... 219 解除阻塞所有线程................................................................................................................... 220 目录 9 相似的同步函数:信号.................................................................................................................. 220 初始化信号............................................................................................................................... 220 增加信号................................................................................................................................... 222 基于信号计数阻塞................................................................................................................... 222 减小信号计数............................................................................................................................ 223 销毁信号状态............................................................................................................................ 224 跨进程边界同步............................................................................................................................... 224 生成方和使用者问题示例...................................................................................................... 224 fork() 和Solaris 线程的特殊问题................................................................................................ 227 9 编程原则........................................................................................................................................... 229 重新考虑全局变量.......................................................................................................................... 229 提供静态局部变量.......................................................................................................................... 230 同步线程........................................................................................................................................... 231 单线程策略232 可重复执行函数232 避免死锁234 与调用相关的死锁 235 锁定原则235 线程代码的一些基本原则236 创建和使用线程 236 使用多处理器237 基础体系结
《Linux多线程服务端编程:使用muduo C++网络库》主要讲述采用现代C++在x86-64 Linux上编写多线程TCP网络服务程序的主流常规技术,重点讲解一种适应性较强的多线程服务器的编程模型,即one loop per thread。 目 录 第1部分C++ 多线程系统编程 第1章线程安全的对象生命期管理3 1.1当析构函数遇到多线程. . . . . . . . . . . . . . . . .. . . . . . . . . . . 3 1.1.1线程安全的定义. . . . . . . . . . . . . . . . .. . . . . . . . . . . 4 1.1.2MutexLock 与MutexLockGuard. . . . . . . . . . . . . . . . . . . . 4 1.1.3一个线程安全的Counter 示例.. . . . . . . . . . . . . . . . . . . 4 1.2对象的创建很简单. . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . 5 1.3销毁太难. . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . 7 1.3.1mutex 不是办法. . . . . . . . . . . . . . . . . . . .. . . . . . . . 7 1.3.2作为数据成员的mutex 不能保护析构.. . . . . . . . . . . . . . 8 1.4线程安全的Observer 有多难.. . . . . . . . . . . . . . . . . . . . . . . . 8 1.5原始指针有何不妥. . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . 11 1.6神器shared_ptr/weak_ptr . . . . . . . . . .. . . . . . . . . . . . . . . . 13 1.7插曲:系统地避免各种指针错误. . . . . . . . . . . . . . . . .. . . . . . 14 1.8应用到Observer 上.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 1.9再论shared_ptr 的线程安全.. . . . . . . . . . . . . . . . . . . . . . . . 17 1.10shared_ptr 技术与陷阱. . . .. . . . . . . . . . . . . . . . . . . . . . . . 19 1.11对象池. . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . 21 1.11.1enable_shared_from_this . . . . . . . . . . . . . . . . . . . . . . 23 1.11.2弱回调. . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . 24 1.12替代方案. . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . 26 1.13心得与小结. . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . 26 1.14Observer 之谬. . . .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 第2章线同步精要 2.1互斥器(mutex). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 2.1.1只使用非递归的mutex . . . . . . . . . . . . . .. . . . . . . . . . 33 2.1.2死锁. . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . 35 2.2条件变量(condition variable). . . . . . . . . .

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值