孫鑫MFC深入詳解 第十五章 多執行緒(一)

2021-06-30 16:10:00 字數 4186 閱讀 5547

windows中的多執行緒程式設計一直是所有程式設計人員感到困難的乙個地方,主要是在windows多執行緒程式設計中,往往要考慮很多的東西,執行緒啟用的多少,執行緒之間的同步問題等等...下面通過模擬火車售票系統來進行講解

#include #include using namespace std ;

dword winapi threadproc1(lpvoid lpparameter) ;

dword winapi threadproc2(lpvoid lpparameter) ;

int index ;

int tickets = 100 ;

handle hmutex ; // 宣告乙個全域性的互斥物件控制代碼

int main()

dword winapi threadproc1(lpvoid lpparameter)

else

break ;

releasemutex(hmutex) ; // 釋放hmutex互斥物件的所有權

} return 0 ;

}dword winapi threadproc2(lpvoid lpparameter)

else

break ;

releasemutex(hmutex) ; }

return 0 ;

}

執行之後,結果如下:

我們可以看到執行緒一和執行緒二之間交替的執行,直到將票賣完。   那麼這樣的結果,肯定是缺少不了執行緒之間的同步,那麼windows程式中,執行緒之間是如何維持執行緒直接的同步的呢??   這也主要靠windows作業系統為我們提供的乙個核心物件 ------> (互斥物件) 來實現的。下面我們將主要分析程式是如何執行的;

分析:

現在應該對互斥物件以及windows是如何通過互斥物件來實現多個執行緒之間的同步的。

我們知道互斥物件使作業系統維護的乙個資料結構,那麼該資料結構都包含什麼東西呢??

-- 互斥物件(mutex)屬於核心物件,它能夠確保執行緒擁有對單個資源的互斥訪問權。

-- 互斥物件包含乙個使用數量,乙個執行緒id和乙個計數器。(如果該互斥物件屬於那個執行緒,則該互斥物件的執行緒id便設定為擁有者執行緒的執行緒id)

-- id用於標識系統中的哪個執行緒當前擁有互斥物件,計數器用於指明該執行緒擁有互斥物件的次數。

可以看到互斥物件除了乙個執行緒id還有乙個計數器,那麼,我們再來看看計數器是怎麼回事以及它是如何運作的,在此之前,我們先來看一段**,如下:

multithread2.cpp

#include #include using namespace std ;

dword winapi threadproc1(lpvoid lpparameter) ;

dword winapi threadproc2(lpvoid lpparameter) ;

int index ;

int tickets = 100 ;

handle hmutex ; // 宣告乙個全域性的互斥物件控制代碼

int main()

dword winapi threadproc1(lpvoid lpparameter)

else

break ;

releasemutex(hmutex) ; // 釋放hmutex互斥物件的所有權

} return 0 ;

}dword winapi threadproc2(lpvoid lpparameter)

else

break ;

releasemutex(hmutex) ; }

return 0 ;

}

和multithread1.cpp的**差不多,只是略微的修改了一下,將multithread1.cpp**中的第26行注釋掉,在第28行重新建立乙個互斥物件(注意這兩行(26和28)**的區別,並且在29行又多新增了一行**,【關於這兩行**的意義,在程式的注釋中寫的很詳細】我們執行程式,看看結果,如下:

程式也是能夠正常的執行,分析可以結合multithread1.cpp程式的分析,自己研究。   

我們繼續向下,multithread2.cpp程式也是能夠正常的執行,但是如果我們在multithread2.cpp程式的第28和第29行之間加上一行**,完整**如下:

multithread3.cpp

#include #include using namespace std ;

dword winapi threadproc1(lpvoid lpparameter) ;

dword winapi threadproc2(lpvoid lpparameter) ;

int index ;

int tickets = 100 ;

handle hmutex ; // 宣告乙個全域性的互斥物件控制代碼

int main()

dword winapi threadproc1(lpvoid lpparameter)

else

break ;

releasemutex(hmutex) ; // 釋放hmutex互斥物件的所有權

} return 0 ;

}dword winapi threadproc2(lpvoid lpparameter)

else

break ;

releasemutex(hmutex) ; }

return 0 ;

}

和上面的兩個程式(multithread1.cpp和multithread2.cpp)一樣,先來看看執行結果,如下:

宣告:由於我的電腦是多核的,造成第一種結果。對於單核電腦,是只可能出現第二種結果的。

來分析一下程式的執行過程,如下:

**中的第28行中建立了乙個互斥物件(hmutex),並且被當前執行緒(主線程main)所擁有(因為creaemutex()函式的第二個引數為true),之後,程式向下執行到waitforsingleobject()函式時, 作業系統會去判斷擁有的互斥物件(hmutex)的執行緒(主線程main)的執行緒id是否等於請求執行緒(此處,請求的執行緒就是主線程main)的執行緒id,如果相等,那麼即使此時互斥物件(hmutex)處於未通知狀態(即非訊號態),當前的請求執行緒(主線程main)也能獲得此互斥物件(hmutex)的所有權,那麼waitforsingleobject()函式就會返回。【 對於乙個執行緒多次擁有乙個互斥物件是通過互斥物件內部的計數器來記錄的】當我們第一次建立互斥物件是(hmutex),主線程擁有了這個互斥物件(hmutex),作業系統除了將該互斥物件(hmutex)的執行緒id設定為主執行緒的執行緒id,同時將該互斥物件(hmutex)的計數器變為1,當用waitforsingleobject()函式去請求該互斥物件(hmutex)時,而此時的互斥物件(hmutex)是處於未通知狀態(即非訊號態)的,但是此時請求互斥物件(hmutex)的執行緒id等於該互斥物件(hmutex)中的執行緒id,所以我們仍能夠請求到該互斥物件,那麼作業系統通過互斥物件中的計數器來記錄我們請求了多少次互斥物件,於是,此時該互斥物件(hmutex)在加1變為2,當我們在呼叫releasemutex()函式時,實際上是將互斥物件內部的計數器進行減1操作,當程式中的互斥物件(hmutex)中的計數器減1操作後,其計數器的值還剩1,這樣主線程還是沒有失去對該互斥物件(hmutex)的的所有權,那麼,該互斥物件(hmutex)還是處於未通知狀態,所以,執行緒一和執行緒二就不能請求到對該互斥物件(hmetex)的所有權, 故執行緒一和執行緒二中的**就不會被執行。

如果想要執行緒一和執行緒二中的**被執行,我們可以在main()函式中再次呼叫releasemutex()函式,將互斥物件(hmetex)的的計數器遞減為0。

我測試程式是可以正常的執行的,這也和我們根據分析得到的結果一致,是不是很高興啊 !!!  具體的**就不貼出來了,程式執行的結果和multthread1.cpp執行的結果一樣。有興趣的話,大家可以自己試一試 !!!

孫鑫MFC深入詳解 第十五章 多執行緒(三)

對命名互斥物件進行例項講解,如下 multithread5.cpp include include using namespace std dword winapi threadproc1 lpvoid lpparameter dword winapi threadproc2 lpvoid lppa...

第十五章預習

public class yuxi15 else 字串的比較 字串1.equals 字串2 比較兩個字串的值是否相同,返回boolean型別的值.如果相同,則返回真值,否則返回假值.字串1.equalsignorecase 字串2 忽略大小寫比較字串1和字串2.如果都相同則返回真值 否則返回假值 改...

VC深入詳解之第十五章多執行緒程式設計

真正完成 執行的是執行緒,而程序知識執行緒的容器,或者說是執行緒的執行環境。執行緒有兩部分組成 執行緒的核心物件和執行緒棧 include include using namespace std dword winapi func1proc lpvoid lpparameter dword wina...