不可中斷的一個或者一系列操做,也就是不會被線程調度機制打斷的操做,在運行期間不會有任何的上下文切換(context switch).java
在多線程操做的時候,肯定某個操做非原子操做,要用消耗性能的昂貴的鎖去保護。linux
在單核CPU中,可以在一個指令中完成的操做均可以看做爲原子操做,由於中斷只發生在指令間。ios
在多核CPU中,軟件級別的原子操做依賴於硬件支持的,在X86體系中,CPU提供了HLOCK pin引 線,容許CPU在執行某個指令(僅僅是一個指令)時拉低HLOCK pin引線的電平,直到這個這個指令執行完畢才放開。拉低電平致使鎖住總線,如此在同一總線的CPU就暫時沒法經過總線訪問內存了,這樣就保證了多核處理器的原子操做。(在那段時間,我猜想,處理器,在想爲啥內存訪問被禁止,什麼鬼,交 通管制,又是哪一個大領導來視察)編程
對於非long和double基本數據類型的「簡單操做」均可以看做原子操做。for example:賦值和返回。在大多數系統中long和double都佔據8個字節,操做系統或者JVM(java虛擬機)極可能會將寫入和讀取操做分離成兩個獨立的操做來執行,這就產生一個讀取和寫入過程當中的上下文切換,從而致使多線程看到不正確的狀況。windows
自增,自減非原子操做,一共三步(兩次內存訪問,一次寄存器修改)多線程
好比:x++;(沒有招,兄弟犧牲點性能吧)架構
mov eax,dword ptr [x] add eax,1 mov dword ptr [x],eax
2、操做系統之上的實現函數
講點沒有用,有大咖測試發現,在linux上進程和線程的效率差很少,window上進程和線程效率差距很大。性能
分別有四種方式:臨界區(CriticalSection)、互斥對象(Mutex)、信號量(Semaphore)、事件對象(Event)。測試
1)臨界區:
使用函數:
CRITICAL_SECTION CriticalSection;
InitializeCriticalSection(&CriticalSection);
EnterCriticalSection(&CriticalSection);
LeaveCriticalSection(&CriticalSection);
DeleteCriticalSection(&CriticalSection);
write an example:
#include <string> #include <iostream> #include < process.h> #include <windows.h> using namespace std; CRITICAL_SECTION g_cs; unsigned __stdcall threadFun(void *param) { EnterCriticalSection(&g_cs); cout<<*(string*)(param)<<endl; LeaveCriticalSection(&g_cs); return 1; } int main() { InitializeCriticalSection(&g_cs); HANDLE hth1, hth2, hth3; string s1 = "first", s2 = "second", s3= "third"; hth1 = (HANDLE)_beginthreadex(NULL, 0, threadFun, &s1, 0, NULL); hth2 = (HNADLE)_beginthreadex(NULL, 0, threadFun, &s2, 0, NULL); hth3 = (HNADLE)_beginthreadex(NULL, 0, threadFun, &s3, 0, NULL); WaitForSingleObject(hth1, INFINITE); WaitForSingleObject(hth2, INFINITE); WaitForSingleObject(hth3, INFINITE); CloseHandle(hth1); CloseHandle(hth2); CloseHandle(hth3); DeleteCriticalSection(&g_cs); return 0; }
2) 互斥對象(Mutex)
引入了對象互斥對象的概念,來保證共享數據操做的完整性,每一個對象對應於一個可稱爲互斥鎖的標記,用這個標記用來保證在任一時刻,只要有一個線程訪問該對象,操做接口:
CreateMutex OpenMutex ReleaseMutex
write an example:
#include<string> #include<iostream> #include<process.h> #include<windows.h> using namespace std; HANDLE hmu; unsined __stdcall threadFun(void *param) { WaitForSingleObject(hmu, INFINITE); cout<<*(string*)(param)<<endl; ReleaseMutex(hmu); return 1; } int main() { hmu = CreateMutex(NULL, FALSE, NULL); HANDLE hth1, hth2, hth3; string s1 = "first", s2 = "second", s3 = "third"; hth1 = (HANDLE)_beginthreadex(NULL, 0, threadFun, &s1, 0, NULL); hth2 = (HANDLE)_beginthreadex(NULL, 0, threadFun, &s2, 0, NULL); hth3 = (HANDLE)_beginthreadex(NULL, 0, threadFun, &s3, 0, NULL); WaitForSingleObjec(hth1, INFINITE); WaitForSingleObjec(hth2, INFINITE); WaitForSingleObject(hth3, INFINITE); CloseHandle(hth1); CloseHandle(hth2); CloseHandle(hth3); CloseHandle(hmu);
return 0; }
3)信號量(Semaphore)
信號量,別稱信號燈。負責協調各個線程,保證正確合理的使用公共資源。
window環境接口:
CreateSemaphore OpenSemaphore ReleaseSemaphore
write an example:
#include <string> #include <iostream> #include <process.h> #include <windows.h> using namespace std; HANDLE hsem1, hsem2, hsem3; usigned __stdcall threadFunA(void *) { for(int i = 0; i < 10; i++) { WaitForSingleObject(hsem1, INFINITE); cout<<"A"; ReleaseSemaphore(hsem2, 1, NULL); } return 1; } unsigned __stdcall threadFunB(void*) { for(int i = 0; i < 10; i++) { WaitForSingleObject(hsem2, INFINITE); cout<<"B"; ReleaseSemaphore(hsem2, 1, NULL); } return 1; } unsigned __stdcall threadFunC(void*) { for(inti =0; i < 10; i++) { WaitForSingleObject(hsem3, INFINITE); cout<<"C"; ReleaseSemphore(hsem1, 1, NULL); } return 1; } int main() { hsem1 = CreateSemaphore(NULL, 1, 1, NULL); hsem2 = CreateSemaphore(NULL, 0, 1, NULL); hsem3 = CreateSemaphore(NULL, 0, 1, NULL); HANDLE hth1, hth2, hth3; hth1 = (HANDLE)_beginthreadex(NULL, 0, threadFunA, NULL, 0, NULL); hth2 = (HANDLE)_beginthreadex(NULL, 0, threadFunB, NULL, 0, NULL); hth3 = (HANDLE)_beginthreadex(NULL, o, threadFunC, NULL, 0, NULl); WaitForSingleObject(hth1, INFINITE); WaitForSingleObject(hth2, INFINITE); WaitForSingleObject(hth3, INFINITE); CloseHandle(hth1); CloseHandle(hth2); CloseHandle(hth3); CloseHandle(hsem1); CloseHandle(hsem2); CloseHandle(hsem3); return 0; }
4) 事件對象(Event)
windows事件操做
CreateEvent OpenEvent PulseEvent ResetEvent SetEvent
主線程通常能夠這樣寫:
CreateEvent(/*...*/); // 建立事件對象 SetEvent(/*...*/); // 設置信號 WaitForMultiObjects(hThread, /*...*/); // 等待線程結束 CloseHandle(/*...*/); // 關閉線程句柄
而被啓動的線程通常要等待某個事件再進行操做:
while(1){ WaitForSingleObject(/*...*/); // 等待事件 /*...*/ }
總結:
1. 使用頻率,信號量 > 互斥對象 > 臨界區 > 事件對象
2. windows編程中有兩種建立線程的方法,一種爲CreateThread(),另外一種爲_beginthreadex(),當若是代碼使用標準C運行庫,建議使用後者。 (window API的好)
無非也是鎖機制,條件變量,讀寫鎖,在linux編程中多線程和多進程差很少,無非API改更名,不作太多探討。
write simple an example:
#include"../apue.h" int pos=0; char *list[10]={"person1","person2","person3","person4","person5", "person6","person7","person8","person9","person10"}; pthread_mutex_t mutex; void *func(void *p) { int a = *(int*)p; for(;;) { pthread_mutex_lock(&mutex); if(pos>=10) { break; } printf("%d號諮詢美女爲%s服務\n",a,list[pos]); pos++; pthread_mutex_unlock(&mutex); sleep(8);//模擬服務時間 } pthread_mutex_unlock(&mutex); } int main() { int i; int a=1,b=2,c=3; pthread_t id1,id2,id3; pthread_mutex_init(&mutex,NULL); pthread_create(&id1,NULL,func,&a); pthread_create(&id2,NULL,func,&b); pthread_create(&id3,NULL,func,&c); pthread_join(id1,NULL); pthread_join(id2,NULL); pthread_join(id3,NULL); while(1); }
參考:網上的博客 (集百家之所長,補本身之所短)
《UNIX環境高級編程》這本書看完了,驗證一遍,linux基本操做都應該沒啥問題。