C++中多线程编程之volatile分析

volatile是c++的关键字之一,其修饰的变量表示容易被修改,要求编译器不要对其读写进行优化。

volatile主要有以下特点:

1:对其修饰的变量,每次读取必须去内存中读取数据,而不相信寄存器或者cache中的数据准确性。

2:vs中,在同一个线程中,volatile修饰的变量的,对于其读操作,在其之后的所有写操作都会在读之后完成,不会被编译器优化乱序执行影响到,对于其写操作,所有在其写之前的读操作都会被在写之前完成,不会被编译器优化影响。所以以下代码的正确性得以保障:

#include 
#include 
using namespace std;
 
volatile bool Sentinel = true;
int CriticalData = 0;
 
unsigned ThreadFunc1( void* pArguments ) {
   while (Sentinel)
      Sleep(0);   // volatile spin lock
 
   // CriticalData load guaranteed after every load of Sentinel
   cout << "Critical Data = " << CriticalData << endl;
   return 0;
} 
 
unsigned  ThreadFunc2( void* pArguments ) {
   Sleep(2000);
   CriticalData++;   // guaranteed to occur before write to Sentinel
   Sentinel = false; // exit critical section
   return 0;
}
 
int main() {
   HANDLE hThread1, hThread2; 
   DWORD retCode;
 
   hThread1 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ThreadFunc1,
      NULL, 0, NULL);
   hThread2 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ThreadFunc2,
      NULL, 0, NULL);
 
   if (hThread1 == NULL || hThread2 == NULL)       {
      cout << "CreateThread failed." << endl; 
      return 1;
   }
 
   retCode = WaitForSingleObject(hThread1,3000);
 
   CloseHandle(hThread1);
   CloseHandle(hThread2);
 
   if (retCode == WAIT_OBJECT_0 && CriticalData == 1 )
      cout << "Success" << endl;
   else
      cout << "Failure" << endl;
}

3:vs中,以上保证跟锁仍然有区别,vs仅仅保证了写之前的读和读之后的写,但是没有保证写之后的读和读之前的写是否会被优化掉,这里就导致卸职后的读和读之前的写的执行顺序不能得到保证,于是会出下下面这种问题:

// volatile.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"

#include 
#include 
using namespace std;

static volatile int flag1 = 0;
static volatile int flag2 = 0;
static volatile int turn = 1; // must have "turn", otherwise the two threads might introduce deadlock at line 13&23 of "while..."
static int gCount = 0;

void dekker1() {
    flag1 = 1;
    turn = 2;
    while ((flag2 == 1) && (turn == 2));
    // critical section
    gCount++;
    flag1 = 0;  // leave critical section
}

void dekker2() {
    flag2 = 1;
    turn = 1;
    while ((flag1 == 1) && (turn == 1));
    // critical setion
    gCount++;
    flag2 = 0;  // leave critical section
}

unsigned ThreadFunc1( void* pArguments ) {
    int i;
    //cout << "Starting Thread 1" << endl;
    for (i=0;i<1000000;i++) {
        dekker1();
    }
    return 0;
} 

unsigned  ThreadFunc2( void* pArguments ) {
    int i;
    //cout << "Starting Thread 2" << endl;
    for (i=0;i<1000000;i++) {
        dekker2();
    }
    return 0;
}

int main() {
    HANDLE hThread1, hThread2;
    //DWORD retCode;

    hThread1 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ThreadFunc1,
        NULL, 0, NULL);
    hThread2 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ThreadFunc2,
        NULL, 0, NULL);

    if (hThread1 == NULL || hThread2 == NULL) {
        cout << "CreateThread failed." << endl;
        return 1;
    }

    WaitForSingleObject(hThread1,INFINITE);
    WaitForSingleObject(hThread2,INFINITE);
    cout << gCount << endl;

    if (gCount == 2000000)
        cout << "Success" << endl;
    else
        cout << "Fail" << endl;

    getchar();
}


这段代码之所以不能正确的输出2000000,是由于两个线程有可能都访问到给gcount++,然后gcount++并不是原子操作,类似于臭名昭著的线程问题:

// volatile.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"

#include 
#include 
using namespace std;

static int gCount = 0;

unsigned ThreadFunc1( void* pArguments ) {
    int i;
    //cout << "Starting Thread 1" << endl;
    for (i=0;i<1000000;i++) {
        gCount++;
    }
    return 0;
} 

unsigned  ThreadFunc2( void* pArguments ) {
    int i;
    //cout << "Starting Thread 2" << endl;
    for (i=0;i<1000000;i++) {
        gCount++;
    }
    return 0;
}

int main() {
    HANDLE hThread1, hThread2;
    //DWORD retCode;

    hThread1 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ThreadFunc1,
        NULL, 0, NULL);
    hThread2 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ThreadFunc2,
        NULL, 0, NULL);

    if (hThread1 == NULL || hThread2 == NULL) {
        cout << "CreateThread failed." << endl;
        return 1;
    }

    WaitForSingleObject(hThread1,INFINITE);
    WaitForSingleObject(hThread2,INFINITE);
    cout << gCount << endl;

    if (gCount == 2000000)
        cout << "Success" << endl;
    else
        cout << "Fail" << endl;

    getchar();
}


因此,务必注意,volatile不能取代锁的作用。

你可能感兴趣的:(vs开发,c++)