先上圖:
分析下這個問題的考察點,主要考察點有二個:
1.主線程建立子線程并傳入一個指向變量位址的指針作參數,由于線程啟動須要花費一定的時間,是以在子線程根據這個指針通路并儲存資料前,主線程應等待子線程儲存完畢後才能改動該參數并啟動下一個線程。這涉及到主線程與子線程之間的同步。
2.子線程之間會互斥的改動和輸出全局變量。要求全局變量的輸出必須遞增。這涉及到各子線程間的互斥。
直接上代碼:
//經典線程同步互斥問題
#include <stdio.h>
#include <process.h>
#include <windows.h>
long g_nNum; //全局資源
unsigned int __stdcall Fun(void *pPM); //線程函數
const int THREAD_NUM = ; //子線程個數
int main()
{
g_nNum = ;
HANDLE handle[THREAD_NUM];
int i = ;
while (i < THREAD_NUM)
{
handle[i] = (HANDLE)_beginthreadex(NULL, , Fun, &i, , NULL);
i++;//等子線程接收到參數時主線程可能改變了這個i的值
}
//保證子線程已全部運作結束
WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
return ;
}
unsigned int __stdcall Fun(void *pPM)
{
//由于建立線程是要一定的開銷的,是以新線程并不能第一時間執行到這來
int nThreadNum = *(int *)pPM; //子線程擷取參數
Sleep();//some work should to do
g_nNum++; //處理全局資源
Sleep();//some work should to do
printf("線程編号為%d 全局資源值為%d\n", nThreadNum, g_nNum);
return ;
}
這是一個既不互斥也不同步的程式,現在要求如下:
1.子線程輸出的線程式号不能重複
2.全局變量的輸出必須遞增
下面這段代碼是我通過原理進行修改的,實作了上面的要求,但是并沒有實作同步,可以加深了解:
#include <stdio.h>
#include <process.h>
#include <windows.h>
#include <iostream>
using namespace std;
volatile long g_nNum; //全局資源
unsigned int __stdcall Fun(void *pPM);
const int THREAD_NUM = ; //子線程個數
int main()
{
g_nNum = ;
HANDLE handle[THREAD_NUM];
int cas = ;
while (cas--){
int i = ;
while (i < THREAD_NUM)
{
Sleep();//***延遲下一個子程序建立,就可以保證前一子程序完成儲存和輸出操作
handle[i] = (HANDLE)_beginthreadex(NULL, , Fun, &i, , NULL);
i++;//***等子線程接收到參數時主線程可能改變了這個i的值
}
//保證子線程已全部運作結束
WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
cout << "case: " << cas << "全局: " << g_nNum << endl;
}
return ;
}
unsigned int __stdcall Fun(void *pPM)
{
//由于建立線程是要一定的開銷的,是以新線程并不能第一時間執行到這來
long nThreadNum = *(long *)pPM; //子線程擷取參數
Sleep();
g_nNum++; //處理全局資源
//InterlockedIncrement((LPLONG)&g_nNum);//這裡采用原子操作但似乎不影響結果
Sleep();//some work should to do
printf("線程編号為%d 全局資源值為%d\n", nThreadNum, g_nNum);//第一個元素是i,第二個元素是全局變量
return ;
}
實作多線程同步的方法有:關鍵段(臨界區),互斥量,信号量,事件等
更詳細的可以去看:http://blog.csdn.net/morewindows/article/details/7442333
值得注意的是:
- 當在同一程序中的多線程同步時,臨界區是效率最最高,基本不需要什麼開銷。而核心對象由于要進行使用者态和核心态的切換,開銷較大,但是核心對象由于可以命名,是以它們同時可以用于程序間的同步。另外,值得一提的是,信号量可以設定允許通路資源的線程或程序個數,而不僅僅是隻允許單個線程或程序通路資源。
- 信号量的使用特點使其更适用于對Socket(套接字)程式中線程的同步。例如,網絡上的HTTP伺服器要對同一時間内通路同一頁面的使用者數加以限制,這時可以為每一個使用者對伺服器的頁面請求設定一個線程,而頁面則是待保護的共享資源,通過使用信号量對線程的同步作用可以確定在任一時刻無論有多少使用者對某一頁面進行通路,隻有不大于設定的最大使用者數目的線程能夠進行通路,而其他的通路企圖則被挂起,隻有在有使用者退出對此頁面的通路後才有可能進入。
- 雖然臨界區同步速度很快,但卻隻能用來同步本程序内的線程,而不可用來同步多個程序中的線程。
- 盡量避免使用者态與核心态的切換
同步方法 | 效率 | 功能 | 屬于哪種模式 |
---|---|---|---|
信号量 | 低 | 控制線程通路數量 | 核心 |
臨界區 | 高 | 程序内線程同步 | 使用者 |
互斥量 | 低 | 程序間資源安全共享,“遺棄”特性 | 核心 |
事件 | 低 | 實作程序間的線程同步 | 核心 |