多線程的些許理解(平臺x86,具體考慮linux,windows)

                  多線程的些許理解

一.體系架構

1.原子操做 

1) 定義

 不可中斷的一個或者一系列操做,也就是不會被線程調度機制打斷的操做,在運行期間不會有任何的上下文切換(context switch).java

2) 咱們爲何關注原子操做(what)

 在多線程操做的時候,肯定某個操做非原子操做,要用消耗性能的昂貴的鎖去保護。linux

3)單核CPU、多核CPU

      在單核CPU中,可以在一個指令中完成的操做均可以看做爲原子操做,由於中斷只發生在指令間。ios

      在多核CPU中,軟件級別的原子操做依賴於硬件支持的,在X86體系中,CPU提供了HLOCK pin引 線,容許CPU在執行某個指令(僅僅是一個指令)時拉低HLOCK pin引線的電平,直到這個這個指令執行完畢才放開。拉低電平致使鎖住總線,如此在同一總線的CPU就暫時沒法經過總線訪問內存了,這樣就保證了多核處理器的原子操做。(在那段時間,我猜想,處理器,在想爲啥內存訪問被禁止,什麼鬼,交 通管制,又是哪一個大領導來視察)編程

 4)如何定義操做原子操做

        對於非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上進程和線程效率差距很大。性能

1.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的好)

2.linux  c 多線程編程

       無非也是鎖機制,條件變量,讀寫鎖,在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基本操做都應該沒啥問題。

相關文章
相關標籤/搜索