线程同步方法:互斥量,信号量,原子锁

///VC, 线程同步方法:互斥量,信号量,原子锁
原子锁:当多个线程同时对同一资源进行操作时,由于线程间资源的抢占,会导致操作的结果丢失或者不是我们预期的结果。

比如:线程A对一个变量进行var++操作,线程B也执行var++操作,当线程A执行var++时,如果线程切换时间恰好是线程A将结果存在var变量之前,那么线程B继续执行var++;此时假设var值已经被线程B更新,这时轮流到线程A执行,线程A会将接着上次停止的点继续向下执行,这时B对var变量的更改将会被覆盖掉;原子锁是对单条操作指令进行原子保护,保证在同一时间,只能有一个线程对变量进行操作,以此确保数据的正确性.

1、InterlockedIncrement:加1操作;
2、InterlockedDecrement:减1操作;
3、InterlockedExchangeAdd:加上“指定”的值,可以加上一个负数;
4、InterlockedExchange、InterlockedExchangePointer:能够以原子操作的方式用第二个参数的值来取代第一个参数的值;

一般情况下,在多线程编程中如果对某一个变量的值进行改变的话,使用以上互锁函数确实比较方便,但有很多时候多线程间会操作更为复杂的东西比如对一个结构的赋值、对链表的插入与删除 等等,以上互锁函数不能满足要求,所以要使用更为高级的多线程间的同步技术!(互斥和信号量和消息队列等)

没有使用原子锁的情况:

#include <Windows.h> 
#include <iostream> 
using namespace std;

#define ThreadNum  2 
#define CIRCLETIME 1000000 
long g_loginCount = 0; 
long g_value = 0;

DWORD WINAPI ThreadProc1( __in  LPVOID lpParameter ) 
{ 
    for( int index=0; index<CIRCLETIME; index++ ) 
    { 
        g_loginCount++; 
    } 
    return 0; 
}

DWORD WINAPI ThreadProc2( __in  LPVOID lpParameter ) 
{ 
    for( int index=0; index<CIRCLETIME; index++ ) 
    { 
        g_loginCount++; 
    } 
    return 0; 
}

int main() 
{ 
    HANDLE handle[ ThreadNum ] = {0};

    handle[0] = CreateThread(NULL,0,ThreadProc1,NULL,0,NULL); 
    handle[1] = CreateThread(NULL,0,ThreadProc2, NULL,0 ,NULL );

    WaitForMultipleObjects(ThreadNum,handle,TRUE,INFINITE); 
    CloseHandle( handle[0] ); 
    CloseHandle( handle[1] );

    cout << "循环之后的值为: " << g_loginCount  << " " <<  g_value << endl; 
    return 0; 
}

运行2次结果分别如下:
循环之后的值为: 1047393 0

循环之后的值为: 1033066 0

使用原子锁的情况:

DWORD WINAPI ThreadProc2( __in  LPVOID lpParameter ) 
{ 
    for( int index=0; index<CIRCLETIME; index++ ) 
    { 
        g_loginCount++; 
        InterlockedIncrement( &g_value  ); 
    } 
    return 0; 
}

DWORD WINAPI ThreadProc1( __in  LPVOID lpParameter ) 
{ 
    for( int index=0; index<CIRCLETIME; index++ ) 
    { 
        g_loginCount++; 
        InterlockedIncrement( &g_value  ); 
    }         
    return 0; 
}

运行2次结果分别如下:
循环之后的值为: 1028567 2000000
循环之后的值为: 1326157 2000000

互斥量,信号量同步例子

#define StartThread(thrFun) CloseHandle(CreateThread(NULL,0,thrFun,NULL,0,NULL))

HANDLE g_hMutex;
int   g_index=0;
int   g_num=2000;


DWORD WINAPI MyThread1( LPVOID lpParam ){
    while (1)
    {
            WaitForSingleObject(g_hMutex,INFINITE);//等待互斥对象有信号
            if (g_num>0)
            {
            g_index++;
            TRACE("\r\n [%03d] [A] NUM=%d",g_index,g_num);
            g_num--; 
            }
            ReleaseMutex(g_hMutex);
            }
    return 0;
}


DWORD WINAPI MyThread2( LPVOID lpParam ){

    while(1){
            WaitForSingleObject(g_hMutex,INFINITE);//等待互斥对象有信号
            if (g_num>0)
            {
            g_index++;
            TRACE("\r\n [%03d] [B] NUM=%d",g_index,g_num);
            g_num--;
            }
            ReleaseMutex(g_hMutex);
    }
    return 0;
}


void CMutex_LockDlg::OnBnClickedOk()
{
    // TODO: 在此添加控件通知处理程序代码
    g_hMutex=CreateMutex(NULL,TRUE,LPCTSTR("tickets"));

    if (g_hMutex)
    {
        if (ERROR_ALREADY_EXISTS==GetLastError())
        {
        TRACE("____Create Mutex fail_____");
        return;
        }
    }

    WaitForSingleObject(g_hMutex,INFINITE);//等待进入互斥体  INFINITE  -1
    ReleaseMutex(g_hMutex);
    ReleaseMutex(g_hMutex);

    StartThread(MyThread1);
    StartThread(MyThread2);
}

信号量(Semaphore)和互斥量一样,属于内核对象。它自动执行可用资源查询的测试,如果有可用资源,则可用资源的计数减少,从而避免其它线程请求资源。当该线程释放该资源后,可用资源计数增加,则操作系统允许另一个线程请求资源。
信号量与临界区和互斥量的不同在于,它不能被认为属于某个线程。也就是说,一个线程可以等待信号量对象(减少它的资源计数),而另一个线程释放该对象(增加它的资源计数)。
Win32 API提供了几个函数用于支持信号量。使用Win32 API产生一个信号量,必须首先调用CreateSemaphore()函数,该函数描述如下:
创建一个信号量
*@param lpSemaphoreAttributes 指定安全属性,如果是NULL就表示使用默认属性。
*@param lInitialCount 用于指定该信号量的初始资源计数,必须大于或等于0,并且小于或等于lMaximumCount。
*@param lMaximumCount 指定信号量的最大资源计数。
*@param lpName 是赋给信号量的字符串名字。
HANDLE CreateSemaphore(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount, LPCTSTR lpName);

你可以根据信号量的字符串名字得到该信号量的句柄:
*@param dwDesiredAccess 访问方式,同互斥量参数。
*@param bInheritHandle 继承特性,同互斥量参数。
*@param lpName 信号量名字。
HANDLE OpenSemaphore(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSTR lpName);

释放信号量函数:
*@param hSemaphore 信号量的句柄。
*@param lReleaseCount指信号量现值的增额,该值必须大于0。
*@param lpPreviousCount 传回信号量原来的计数,可以为NULL。
BOOL ReleaseSemaphore(HANDLE hSemaphore, LONG lReleaseCount, LPLONG lpPreviousCount);

释放信号量函数与释放互斥量函数形式相同,但不同之处在于:
一、任意线程可以在任意时刻调用此函数,因为信号量对象不为某个线程拥有。
二、ReleaseSemaphore()函数可以用大于1的大小来增加信号量的资源计数。


#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
#include <string.h>
#include <stdlib.h>
#define  threadnum 10
typedef struct THREADDATA
{
   int id;
   char name[10];
   int sleep;
}THREADDATA;

HANDLE handleSemaphore;
char * str;
DWORD WINAPI ThreadProc( LPVOID lpParam )
{
   THREADDATA *data=(THREADDATA *)lpParam;

   WaitForSingleObject(handleSemaphore,INFINITE);
   for(int i=0;i<10;i++)
   {
 // WaitForSingleObject(handleSemaphore,INFINITE);
   printf("thread%d:%d\n",data->id,i);
 // ReleaseSemaphore(handleSemaphore,1,NULL);
   Sleep(data->sleep);
   }
   ReleaseSemaphore(handleSemaphore,1,NULL);
   return 0;
}
int main(int argc, char* argv[])
{
   str=(char*)malloc(30);
   THREADDATA  pData[threadnum];
   DWORD dwThreadId[threadnum];
   HANDLE hThread[threadnum];
   handleSemaphore=CreateSemaphore(NULL,1, 2,"thread");
   for(int i=0;i<threadnum;i++)
   {
     pData[i].id=i;
     sprintf(pData[i].name,"yuguoqing");
     pData[i].sleep=i*10;
     hThread[i] = CreateThread(NULL,0,ThreadProc, pData+i, 0,  dwThreadId+i);
   }
    WaitForMultipleObjects(threadnum, hThread, TRUE, INFINITE);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/wabil/article/details/51593321