windows平台多线程同步之Mutex的应用

原创 2015年07月12日 18:27:23
**windows平台多线程同步之Mutex的应用** ———————-
- **前言** **线程组成**: 1. 线程的内核对象,操作系统用来管理该线程的数据结构。 2. 线程堆栈,它用于维护线程在执行代码时需要的所有参数和局部变量。   操作系统为每一个运行线程安排一定的CPU时间 —— **时间片**。系统通过一种循环的方式为线程提供时间片,线程在自己的时间内运行,多个线程不断地切换运行,因时间片相当短,因此,给用户的感觉,就好像线程是同时运行的一样。   单cpu计算机一个时间只能运行一个线程,如果计算机拥有多个CPU,线程就能真正意义上同时运行了。   windows平台下,创建线程可以使用windows api 函数CreateThread来实现,函数声明是:
WINBASEAPI
HANDLE
WINAPI
CreateThread(
    LPSECURITY_ATTRIBUTES lpThreadAttributes,
    DWORD dwStackSize,
    LPTHREAD_START_ROUTINE lpStartAddress,
    LPVOID lpParameter,
    DWORD dwCreationFlags,
    LPDWORD lpThreadId
    );
参数说明:
lpThreadAttributes 线程安全性,使用缺省安全性,一般缺省null
dwStackSize 堆栈大小,0为缺省大小
lpStartAddress 线程要执行的函数指针,即入口函数
lpParameter 线程参数
dwCreationFlags 线程标记,如为0,则创建后立即运行
lpThreadId LPDWORD为返回值类型,一般传递地址去接收线程的标识符,一般设为null
因为要使用windows api函数,所以包含:
#include <windows.h>
另外需要标准输入输出函数,所以包含:
#include <iostream.h>
- 问题引出   以多个售票窗口卖同一张火车票为例,定义一个全局的票数tickets,用两个线程来执行卖票,两个线程访问同一个变量tickets,先看一个不正确的写法:
//问题程序

#include <windows.h>
#include <iostream.h>

DWORD WINAPI Fun1Proc(
  LPVOID lpParameter   
);

DWORD WINAPI Fun2Proc(
  LPVOID lpParameter   
);


int tickets=100;

void main()
{
    HANDLE hThread1;
    HANDLE hThread2;
    hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
    hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);

    CloseHandle(hThread1);
    CloseHandle(hThread2);

    system("pause");
}

DWORD WINAPI Fun1Proc(
  LPVOID lpParameter   
)
{
    while(TRUE)
    {
        if(tickets>0)
        {
            Sleep(1);//假定为卖票需要花费的时间
            cout<<"thread1 sell ticket : "<<tickets--<<endl;
        }
        else
            break;  
    }
    return 0;
}

DWORD WINAPI Fun2Proc(
  LPVOID lpParameter   // thread data
)
{   
    while(TRUE)
    {
        if(tickets>0)
        {
            Sleep(1);
            cout<<"thread2 sell ticket : "<<tickets--<<endl;
        }
        else
            break;  
    }
    return 0;
}
  线程中sleep(1);表名该线程放弃执行的权利,操作系统会选择另外的线程进行执行。所以执行结果是:

执行结果


  可以看见程序不是按照预期的效果执行的,tickets的改变是混乱的。所以两个线程访问同一块资源时,需要考虑线程同步问题,即其中一个线程操作改资源时,其他线程不能访问该资源,只能等待,该线程执行结束之后,其他线程才能对该资源进行访问。
  一般采用互斥对象来实现线程的同步。
  • 互斥对象
    特征
      互斥对象(mutex)属于内核对象,它能够确保线程拥有对单个资源的互斥访问权。
      互斥对象包含一个使用数量,一个线程ID和一个计数器。
      ID用于标识系统中的哪个线程当前拥有互斥对象,计数器用于指明该线程拥有互斥对象的次数。
      采用互斥对象进行多线程同步的正确例子如下:
#include <windows.h>
#include <iostream.h>

DWORD WINAPI Fun1Proc(
  LPVOID lpParameter   // thread data
);

DWORD WINAPI Fun2Proc(
  LPVOID lpParameter   // thread data
);
int index=0;
int tickets=100;
HANDLE hMutex;
void main()
{
    HANDLE hThread1;
    HANDLE hThread2;
    hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
    hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);
    CloseHandle(hThread1);
    CloseHandle(hThread2);

    //创建一个匿名的互斥对象,且为有信号状态,
    hMutex=CreateMutex(NULL,FALSE,NULL);

    system("pause");

}

DWORD WINAPI Fun1Proc(
  LPVOID lpParameter   // thread data
)
{
    while(TRUE)
    {
        //等待互斥对象的信号,INFINITE表示一直等待,对之后的代码进行保护
        WaitForSingleObject(hMutex,INFINITE);
        if(tickets>0)
        {
            Sleep(1);
            cout<<"thread1 sell ticket : "<<tickets--<<endl;
        }
        else
            break;
        //释放指定互斥对象的所有权,操作系统将互斥对象的线程id被置为0,互斥对象变为已通知状态,线程2就能请求到互斥对象
        ReleaseMutex(hMutex);
    }
    return 0;
}

DWORD WINAPI Fun2Proc(
  LPVOID lpParameter   // thread data
)
{
    while(TRUE)
    {
        WaitForSingleObject(hMutex,INFINITE);
        if(tickets>0)
        {
            Sleep(1);
            cout<<"thread2 sell ticket : "<<tickets--<<endl;
        }
        else
            break;
        ReleaseMutex(hMutex);
    }
    return 0;
}

执行结果


  通过测试可知,以上互斥对象的引入可以很好的解决线程间访问资源同步的问题。关于互斥对象,还有以下几个问题需要说明。
  • 互斥对象的释放问题
      如果main中
    hMutex=CreateMutex(NULL,TRUE,NULL);

  子线程中:

    while(TRUE)
    {
        ReleaseMutex(hMutex);//无效
        //等待互斥对象的信号,INFINITE表示一直等待,对之后的代码进行保护
        WaitForSingleObject(hMutex,INFINITE);
    }

  如果CreateMutex第二个参数为true,则表示主线程拥有该互斥对象,操作系统将互斥对象的线程id设为主线程的线程id,如果主线程不释放,则子线程会一直等待,此时子线程也没有权利进行释放,所以使用互斥对象的原则是:谁拥有互斥对象,谁释放互斥对象。

  另外,如果main中

    hMutex=CreateMutex(NULL,TRUE,NULL);

  并且再次请求互斥对象:

    WaitForSingleObject(hMutex,INFINITE);

  并调用一次释放互斥对象:

    ReleaseMutex(hMutex);

  此时子线程依然是等待状态,得不到互斥对象的使用权,原因是:
  CreateMutex(NULL,TRUE,NULL)由于第二个参数为true,主线程拥有互斥对象的使用权,互斥对象内部计数器加1,再次调用WaitForSingleObject请求互斥对象时,内部计数器又加1,计数器是记录线程拥有互斥对象的次数,而只释放ReleaseMutex了一次,互斥对象依然被占用,所以子线程得不到使用权。
  因此正确的写法是:

    hMutex=CreateMutex(NULL,TRUE,NULL);
    WaitForSingleObject(hMutex,INFINITE);
    ReleaseMutex(hMutex);
    ReleaseMutex(hMutex);

  如果多次请求互斥对象,就应该多次释放互斥对象。

  再看这样一种情况,线程中没有释放互斥对象的拥有权:

    DWORD WINAPI Fun1Proc(LPVOID lpParameter)
    {
        waitforsingleobject(hmutex,infinite);
        cout<<"thread1 is running"<<endl;
        return 0;
    }
    DWORD WINAPI Fun2Proc(LPVOID lpParameter)
    {
        waitforsingleobject(hmutex,infinite);
        cout<<"thread2 is running"<<endl;
        return 0;
    }

  此时执行依然能够得到输出:

    "thread1 is running
    "thread2 is running

  这是因为:如果线程退出时没有释放互斥对象,操作系统在销毁线程时会自动将线程占用的互斥对象的信息清除,计数器归零,这样其他线程(thread2 )就能申请到互斥对象使用权。

  • 创建命名互斥对象
    hMutex=CreateMutex(NULL,TRUE,"myApp");
    if(hMutex)
    {
        if(ERROR_ALREADY_EXISTS==GetLastError())
        {
            cout<<"已经有一个相同应用程序在运行!"<<endl;
            return;
        }
    }

  命名互斥对象的一种应用是:通过命名互斥对象,可以保证当前只有一个应用程序实例在运行。

  以上是关于windows平台下多线程同步相关的互斥对象的使用问题,之后将对线程同步的事件对象Event进行介绍和解析,敬请关注。文中如有谬误,还望不吝赐教。

点击博客新址

版权声明:本文为csdn博主[applebite](http://blog.csdn.net/applebite) 原创文章,转载请注明出处,侵权必究。 微信添加公众号:qi_chuang 或 猎奇创新平台 https://blog.csdn.net/applebite/article/details/46852149

Windows线程同步之互斥锁(Mutex)

线程同步的方式和机制 临界区、互斥区、事件、信号量四种方式 临界区(Critical Section)、互斥量(Mutex)、信号量(Semaphore)、事件(Event)的区别 1、临界...
  • changyourmind
  • changyourmind
  • 2016-08-16 11:23:34
  • 5055

C++多线程-第二篇-Mutex(互斥量)

//Boost #include #define BOOST_THREAD_VERSION 4 //使用最新版本,含有1,2,3但只是为了兼容之前程序。 Thread库丰富强大的扩展功能但不在Thre...
  • hffhjh111
  • hffhjh111
  • 2016-11-12 16:10:58
  • 9083

Windows下CriticalSection和Mutex的性能比较

原文地址:http://blog.csdn.net/dreamfreelancer/article/details/4237272 Windows下100万次加/解锁测试: Critica...
  • c1520006273
  • c1520006273
  • 2016-01-19 13:17:14
  • 889

Windows同步API—— Mutex

互斥 互斥量的作用是保证每次只能有一个线程获得互斥量而得以继续执行,使用CreateMutex 函数创建: HANDLE CreateMutex( LPSECURITY_ATTRI...
  • yockie
  • yockie
  • 2013-08-01 16:10:50
  • 2310

Windows 多线程 Mutex

不使用互斥 2个线程输出数组arr#include #include #include using namespace std; HANDLE hMutex = NULL; int...
  • leonardWang
  • leonardWang
  • 2010-07-18 22:33:00
  • 4315

用C++和Windows的互斥对象(Mutex)来实现线程同步锁

//这是2个线程模拟卖火车票的小程序 #include #include DWORD WINAPI Fun1Proc(LPVOID lpParameter);//thread data DWOR...
  • jiangxinyu
  • jiangxinyu
  • 2012-07-17 11:16:45
  • 10126

Windows进程间互斥锁

Windows的互斥锁Mutex是可以在进程间使用的。     CreateMutex时可以指定一个mutex名称,此名称可以被其他进程或线程使用。CreateMutex的第二个参数BOOL bIn...
  • tobacco5648
  • tobacco5648
  • 2014-06-10 17:12:33
  • 25198

Windows编程--互斥量Mutex操作函数

Windows编程–互斥量Mutex操作函数转载自百度百科:CreateMutex,OpenMutex常用操作mutex的函数有:ReleaseMutex / OpenMutex / WaitForS...
  • libing403
  • libing403
  • 2017-08-10 23:13:25
  • 539

互斥锁mutex的使用方法

在线程实际运行过程中,我们经常需要多个线程保持同步。这时可以用互斥锁来完成任务;互斥锁的使用过程中,主要有pthread_mutex_init,pthread_mutex_destory,pthrea...
  • zgaoq
  • zgaoq
  • 2017-02-08 14:56:18
  • 3744
收藏助手
不良信息举报
您举报文章:windows平台多线程同步之Mutex的应用
举报原因:
原因补充:

(最多只允许输入30个字)