亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

Win32下兩種用于C++的線程同步類(上)

2019-11-17 05:00:17
字體:
來源:轉載
供稿:網友
線程同步是多線程程序設計的核心內容,它的目的是正確處理多線程并發時的各種問題,例如線程的等待、多個線程訪問同一數據時的互斥,防死鎖等。Win32提供多種內核對象和手段用于線程同步,如互斥量、信號量、事件、臨界區等。所不同的是,互斥量、信號量、事件都是Windows的內核對象,當程序對這些對象進行控制時會自動轉換到核心態,而臨界區本身不是內核對象,它是工作在用戶態的。我們知道從用戶態轉換到核心態是需要以時間為代價的,所以假如能在用戶態就簡單解決的問題,就可以不必勞煩核心態了。

  這里我要說的是兩種用于C++的多線程同步類,通過對這兩種類的使用就可以方便的實現對變量或代碼段的加鎖控制,從而防止多線程對變量不正確的操作。

  所謂加鎖,就是說當我們要訪問某要害變量之前,都需要首先獲得答應才能繼續,假如未獲得答應則只有等待。一個要害變量擁有一把鎖,一個線程必須先得到這把鎖(其實稱為鑰匙可能更形象)才可以訪問這個變量,而當某個變量持有這把鎖的時候,其他線程就不能重復的得到它,只有等持有鎖的線程把鎖歸還以后其他線程才有可能得到它。之所以這樣做,就是為了防止一個線程讀取某對象途中另一線程對它進行了修改,或兩線程同時對一變量進行修改,例如:

// 全局:
strUCt MyStruct ... { int a, b; } ;
MyStruct s;
// 線程1:
int a = s.a;
int b = s.b;
// 線程2:
s.a ++ ;
s.b -- ;
  假如實際的執行順序就是上述書寫的順序那到沒有什么,但假如線程2的執行打斷了線程1,變為如下順序:

int a = s.a; //線程1
s.a++; //線程2
s.b++; //線程2
int b = s.b; //線程1
  那么這時線程1讀出來的a和b就會有問題了,因為a是在修改前讀的,而b是在修改后讀的,這樣讀出來的是不完整的數據,會對程序帶來不可預料的后果。天知道兩個程的調度順序是什么樣的。為了防止這種情況的出現,需要對變量s加鎖,也就是當線程1得到鎖以后就可以放心的訪問s,這時假如線程2要修改s,只有等線程1訪問完成以后將鎖釋放才可以,從而保證了上述兩線程交叉訪問變量的情況不會出現。

  使用Win32提供的臨界區可以方便的實現這種鎖:

// 全局:
CRITICAL_SECTION cs;
InitializeCriticalSection( & cs);
// 線程1:
EnterCriticalSection( & cs);
int a = s.a;
int b = s.b;
LeaveCriticalSection( & cs);
// 線程2:
EnterCriticalSection( & cs);
s.a ++ ;
s.b -- ;
LeaveCriticalSection( & cs);
// 最后:
DeleteCriticalSection( & cs);
  代碼中的臨界區變量(cs)就可以看作是變量s的鎖,當函數EnterCriticalSection返回時,當前線程就獲得了這把鎖,之后就是對變量的訪問了。訪問完成后,調用LeaveCriticalSection表示釋放這把鎖,答應其他線程繼續使用它。

  假如每當需要對一個變量進行加鎖時都需要做這些操作,顯得有些麻煩,而且變量cs與s只有邏輯上的鎖關系,在語法上沒有什么聯系,這對于鎖的治理帶來了不小的麻煩。程序員總是最懶的,可以想出各種偷懶的辦法來解決問題,例如讓被鎖的變量與加鎖的變量形成物理上的聯系,使得鎖變量成為被鎖變量不可分割的一部分,這聽起來是個好主意。

  首先想到的是把鎖封閉在一個類里,讓類的構造函數和析構函數來治理對鎖的初始化和鎖毀動作,我們稱這個鎖為“實例鎖”:

class InstanceLockBase
... {
CRITICAL_SECTION cs;
PRotected :
InstanceLockBase() ... { InitialCriticalSection( & cs); }
~ InstanceLockBase() ... { DeleteCriticalSection( & cs); }
} ;
  假如熟悉C++,看到這里一定知道后面我要干什么了,對了,就是繼續,因為我把構造函數和析構函數都聲明為保護的(protected),這樣唯一的作用就是在子類里使用它。讓我們的被保護數據從這個類繼續,那么它們不就不可分割了嗎:

struct MyStruct: public InstanceLockBase
... { … } ;
  什么?結構體還能從類繼續?當然,C++中結構體和類除了成員的默認訪問控制不同外沒有什么不一樣,class能做的struct也能做。此外,也許你還會問,假如被鎖的是個簡單類型,不能繼續怎么辦,那么要么用一個類對這個簡單類型進行封裝(記得java里有int和Integer嗎),要么只好手工治理它們的聯系了。假如被鎖類已經有了基類呢?沒關系,C++是答應多繼續的,多一個基類也沒什么。

  現在我們的數據里面已經包含一把鎖了,之后就是要添加加鎖和解鎖的動作,把它們作為InstanceLockBase類的成員函數再合適不過了:

class InstanceLockBase
... {
 CRITICAL_SECTION cs;
 void Lock() ... { EnterCriticalSection( & cs); }
 void Unlock() ... { LeaveCriticalSection( & cs); }
 …
} ;
  看到這里可能會發現,我把Lock和Unlock函數都聲明為私有了,那么如何訪問這兩個函數呢?是的,我們總是需要有一個地方來調用這兩個函數以實現加鎖和解鎖的,而且它們總應該成對出現,但C++語法本身沒能限制我們必須成對的調用兩個函數,假如加完鎖忘了解,那后果是嚴重的。這里有一個例外,就是C++對于構造函數和析構函數的調用是自動成對的,對了,那就把對Lock和Unlock的調用專門寫在一個類的構造函數和析構函數中:


class InstanceLock
... {
 InstanceLockBase * _pObj;
 public :
  InstanceLock(InstanceLockBase * pObj)
  ... {
   _pObj = pObj; // 這里會保存一份指向s的指針,用于解鎖
   if (NULL != _pObj)
   _pObj -> Lock(); // 這里加鎖
  }
  ~ InstanceLock()
  ... {
   if (NULL != _pObj)
   _pObj -> Unlock(); // 這里解鎖
 }
} ;
  最后別忘了在類InstanceLockBase中把InstanceLock聲明為友元,使得它能正確訪問Lock和Unlock這兩個私有函數:

class InstanceLockBase
... {
 friend class InstanceLock;
 …
} ;
  好了,有了上面的基礎,現在對變量s的加解鎖治理變成了對InstanceLock的實例的生命周期的治理了。假如我們有一個函數ModifyS中要對s進行修改,那么只要在函數一開始就聲明一個InstaceLock的實例,這樣整個函數就自動對s加鎖,一旦進入這個函數,其他線程就都不能獲得s的鎖了:

void ModifyS()
... {
 InstanceLock lock ( & s); // 這里已經實現加鎖了
 // some Operations on s
} // 一旦離開lock對象的作用域,自動解鎖
  假如是要對某函數中一部分代碼加鎖,只要用一對大括號把它們括起來再聲明一個lock就可以了:


... {
 InstanceLock lock ( & s);
 // do something …
}

  好了,就是這么簡單。下面來看一個測試。

  首先預備一個輸出函數,對我們理解程序有幫助。它會在輸出我們想輸出的內容同時打出行號和時間:

void Say( char * text)
... {
 static int count = 0 ;
 SYSTEMTIME st;
 ::GetLocalTime( & st);
 printf( " %03d [%02d:%02d:%02d.%03d]%s " , ++ count, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds, text);
}
  當然,原則上當多線程都調用這個函數時應該對其靜態局部變量count進行加鎖,這里就省略了。

  我們聲明一個非常簡單的被鎖的類型,并生成一個實例:

class MyClass: public InstanceLockBase
... {} ;
MyClass mc;
  子線程的任務就是對這個對象加鎖,然后輸出一些信息:

DWord CALLBACK ThreadProc(LPVOID param)
... {
 InstanceLock il( & mc);
 Say( " in sub thread, lock " );
 Sleep( 2000 );
 Say( " in sub thread, unlock " );
 return 0 ;
}
  這里會輸出兩條信息,一是在剛剛獲得鎖的時間,二是在釋放鎖的時候,中間通過Sleep來延遲2秒。

  主線程負責開啟子線程,然后也對mc加鎖:

CreateThread( 0 , 0 , ThreadProc, 0 , 0 , 0 );
... {
 InstanceLock il( & mc);
 Say( " in main thread, lock " );
 Sleep( 3000 );
 Say( " in main thread, lock " );
}
  運行此程序,得到的輸出如下:

001 [13:43:23.781]in main thread, lock
002 [13:43:26.781]in main thread, lock
003 [13:43:26.781]in sub thread, lock
004 [13:43:28.781]in sub thread, unlock
  從其輸出的行號和時間可以清楚的看到兩個線程間的互斥:當主線程恰好首先獲得鎖時,它會延遲3秒,然后釋放鎖,之后子線程才得以繼續進行。這個例子也證實我們的類工作的很好。

  總結一下,要使用InstanceLock系列類,要做的就是:

  1、讓被鎖類從InstanceLockBase繼續

  2、所有要訪問被鎖對象的代碼前面聲明InstanceLock的實例,并傳入被鎖對象的指針。

  附:完整源代碼:

#pragma once
#include < windows.h >

class InstanceLock;

class InstanceLockBase
... {
 friend class InstanceLock;

 CRITICAL_SECTION cs;

 void Lock()
 ... {
  ::EnterCriticalSection( & cs);
 }

 void Unlock()
 ... {
  ::LeaveCriticalSection( & cs);
 }

 protected :
 InstanceLockBase()
 ... {
  ::InitializeCriticalSection( & cs);
 }

 ~ InstanceLockBase()
 ... {
  ::DeleteCriticalSection( & cs);
 }
} ;


class InstanceLock
... {
 InstanceLockBase * _pObj;
 public :
  InstanceLock(InstanceLockBase * pObj)
  ... {
   _pObj = pObj;
   if (NULL != _pObj)
    _pObj -> Lock();
  }

 ~ InstanceLock()
 ... {
  if (NULL != _pObj)
   _pObj -> Unlock();
 }
} ;

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久久久国产视频| 97精品一区二区视频在线观看| 日本国产高清不卡| 欧美中文在线观看国产| 久久人人爽人人爽人人片亚洲| 九九精品在线观看| 狠狠爱在线视频一区| 国产精品欧美日韩一区二区| 黑人狂躁日本妞一区二区三区| 久久伊人91精品综合网站| 欧美午夜精品久久久久久久| 国产精品网红福利| 国产91精品久久久| 欧美日韩国内自拍| 亚洲www永久成人夜色| 国产欧美精品va在线观看| 色狠狠久久aa北条麻妃| 欧美区二区三区| 欧美激情综合亚洲一二区| 亚洲一区二区三区xxx视频| 国产成人av网址| 欧美人与物videos| 色综合色综合久久综合频道88| 亚洲国产精品久久久久秋霞蜜臀| 欧美激情精品久久久久| 精品国产老师黑色丝袜高跟鞋| 国产精品视频免费观看www| 亚洲a成v人在线观看| 最近中文字幕mv在线一区二区三区四区| 亚洲一区二区三区xxx视频| 久久视频在线免费观看| 精品久久久久久亚洲精品| 久久人人爽人人爽人人片av高请| 日韩电影中文字幕在线观看| 91视频九色网站| 久久国产精品电影| 国产精品av电影| 日韩小视频在线| 成人精品在线观看| 亚洲精品免费在线视频| 久久久午夜视频| 久久久久久久久久亚洲| 欧美做受高潮1| 国产乱肥老妇国产一区二| 深夜福利一区二区| 国产精品91在线观看| 欧美精品18videos性欧美| 久久精品免费电影| 成人激情电影一区二区| 38少妇精品导航| 亚洲女在线观看| 在线视频欧美日韩精品| 91精品国产综合久久香蕉| 欧美日韩另类字幕中文| 色狠狠av一区二区三区香蕉蜜桃| 亚洲综合一区二区不卡| 中文字幕日韩高清| 在线观看国产精品淫| 精品久久久久久| 欧美美最猛性xxxxxx| 亚洲精品国产综合区久久久久久久| 亚洲xxx大片| 91干在线观看| 欧美大片在线免费观看| 久久久久久国产免费| www.99久久热国产日韩欧美.com| 欧美又大粗又爽又黄大片视频| 色妞一区二区三区| 欧美在线激情网| 黑人巨大精品欧美一区二区一视频| 国产一区二区三区网站| 欧美大胆a视频| 欧美一级淫片aaaaaaa视频| 欧美日韩国产在线播放| 精品国产31久久久久久| 色妞色视频一区二区三区四区| 国产丝袜精品视频| 97久久久久久| 欧美性视频精品| 亚洲国产欧美一区二区三区同亚洲| 日韩免费av一区二区| 亚洲精品自拍偷拍| 国产精品永久免费观看| 不用播放器成人网| 国产精品国产亚洲伊人久久| 中文字幕不卡av| 中文字幕久久亚洲| 日韩av中文字幕在线播放| 91色在线观看| 亚洲成人性视频| 欧美成在线观看| 国产精品偷伦一区二区| 欧美激情综合亚洲一二区| 亚洲国产高潮在线观看| 久久精品国产成人精品| 亚洲综合中文字幕在线| 欧美丰满片xxx777| 亚洲国产精品va| 中文字幕日韩欧美在线| 国产91成人video| 国产亚洲精品久久久久久牛牛| 久久色在线播放| 国外色69视频在线观看| 国产福利精品av综合导导航| 欧美成人免费视频| 亚洲欧美中文字幕在线一区| 日本欧美一级片| 久久久免费av| 欧美—级a级欧美特级ar全黄| 国产成人午夜视频网址| 欧美专区中文字幕| 欧美有码在线观看| 亚洲欧美日韩一区二区三区在线| 亚洲男人天堂古典| 亚洲片在线观看| 日韩av电影免费观看高清| 成人黄色免费看| 国产日韩专区在线| 亚洲成色www8888| 国产精品美女呻吟| 亚洲国产欧美精品| 国产色婷婷国产综合在线理论片a| 亚洲www永久成人夜色| 日av在线播放中文不卡| 国产精品久久久久久中文字| 欧美激情一级二级| 亚洲国产一区二区三区四区| 久久精品中文字幕电影| 日韩电影在线观看永久视频免费网站| 久久综合88中文色鬼| 欧美极品少妇xxxxⅹ免费视频| 国产一区二区三区在线视频| 国内精久久久久久久久久人| 亚洲精品久久在线| 亚洲美女黄色片| 国产精品自拍偷拍视频| 欧美成aaa人片在线观看蜜臀| 亚洲第一区在线| 2019中文字幕在线免费观看| 久久久亚洲精品视频| 精品中文字幕久久久久久| 日韩a**中文字幕| 欧美日韩免费区域视频在线观看| 欧美富婆性猛交| 国产精品视频区1| 国产精品99久久99久久久二8| 欧美视频在线观看免费| 国产精品久久久久av免费| 国产精品久久久久久久久久久久久| 欧美网站在线观看| 91亚洲国产成人久久精品网站| 久久精品中文字幕一区| 日韩中文字幕久久| 日本久久久久亚洲中字幕| 久久中文精品视频| 国产精品久久久久久久久久新婚| 亚洲欧美日韩综合| xxx一区二区| 91久久嫩草影院一区二区| 日韩理论片久久| 日韩不卡在线观看| 亚洲免费伊人电影在线观看av| 萌白酱国产一区二区| 国产精品久久久久久久av大片|