天天看點

線程的建立及互斥變量

MFC多線程程式設計

一、建立線程函數:

函數原型:

HANDLEWINAP lCreateThread(

LPSECURITY_ATTRIBUTES lpThreadAttributes,

SIZE_T dwStackSize,

LPTHREAD_START_ROUTINE lpStartAddress,

LPVOID lpParameter,

DWORD dwCreationFlags,

LPDWORD lpThreadld);

參數說明:

第一個參數表示線程核心對象的安全屬性,一般傳入NULL表示使用預設設定。

第二個參數表示線程空間大小。傳入0表示使用預設大小(1MB)。

第三個參數表示新線程所執行的線程函數的位址,多個線程可以使用同一個函數位址。

一、建立線程函數:

函數原型:

HANDLEWINAP lCreateThread(

LPSECURITY_ATTRIBUTES lpThreadAttributes,

SIZE_T dwStackSize,

LPTHREAD_START_ROUTINE lpStartAddress,

LPVOID lpParameter,

DWORD dwCreationFlags,

LPDWORD lpThreadld

);

第四個參數是傳給線程函數的參數。

第五個參數指定額外的标志來控制線程的建立,為0表示線程建立之後立即就可以進行排程,如果為CREATE_SUSPENDED則表示線程建立後暫停運作,這樣它就無法排程,知道ResumeThread().

第六個參數将傳回線程的ID号,傳入NULL表示不需要傳回該線程的ID号。

函數傳回值:成功後傳回新線程的句柄,失敗傳回NULL。

二、WaitForSingleObject

函數功能:等待函數-使線程進入等待狀态,直到指定的核心對象被觸發。

函數原型:

DWORD WINAPI WaitForSingleObeject(

HANDLE hHandle,

DWORD dwMilliSeconds

);

參數說明:

第一個參數為要等待的核心對象。

第二個參數為最長的等待時間,以毫秒為機關,如傳入5000就表示5秒,傳入0就立即傳回。傳入INFINITE就表示無限等待。

函數傳回值:

在指定的時間内觸發,函數傳回WAIT_OBJECT_0。

超過最長等待時間對象仍未被觸發傳回WAIT_TIMEOUT.傳入參數有錯誤将傳回WAIT_FAILED

例子:

//子線程函數

DWORD WINAPI ThreadFun(LPVOID pM)

{

cout << “子線程的線程ID号為:”<< GetCurrentThreadId() <<"\n子線程輸出Hello World : " << endl;

return 0;

}

//主函數:主線程執行函數

int main()

{

cout << "建立多線程\n ";

cout << “更多的windows32\n”;

HANDLE handle = CreateThread(NULL, 0, ThreadFun, NULL, 0, NULL);
WaitForSingleObject(handle, INFINITE); 
system("pause");
return 0;
           

}

共享變量問題:(當線程A正在函數中運作的時候,此時線程B進來了,他改動了每個變量,這是就有可能會影響線程A的運作。

解決方案:讓每個線程都擁有自己專用的一塊記憶體區域來供标準C運作庫中所有有需要的函數使用。

這塊記憶體區域的建立有C/C++運作庫函數_beginThreadex()負責。

子線程函數。

_beginThreadex()函數

在建立新線程時會配置設定并初始化一個_tiddata塊。這個塊自然是用來存放一些需要線程獨享的資料。

新線程運作是會首先将_tiddata塊與自己進一步關聯起來

這樣每一個線程就隻會通路和修改自己的資料而不會去串改其他線程的資料了。

如果在代碼中使用标準的C運作庫中的函數時,盡量使用_beginThreadex()來代替CreateThread().

例子:

unsigned int _stdcall ThreadFun(LPVOID pM)

{

cout << “線程ID号為” << GetCurrentThreadId()<<"的子線程說: Hello World \n ";

return 0;

}

//主函數:主線程執行函數

int main()

{

const int THREAD_NUM = 5;

HANDLE handle[THREAD_NUM];

for (int i = 0; i < THREAD_NUM;i++)

{

handle[i] = (HANDLE)_beginthreadex(NULL, 0, ThreadFun, NULL, 0, NULL);

}

WaitForMultipleObjects(THREAD_NUM, handle, true, INFINITE);

system(“pause”);

return 0;

}

舉個例子:

DWORD WINAPI FunProc(LPVOID lpParameter)

{

while(true)

{

if(tickets>0)

{

cout<<"Thread 1 sell ticket: "<<tickets–<<endl;

}

else

break;

}

return 0;

}

上面這個函數例子是一個火車票售票的的例子。

雖然可以使每個線程通路他們自己的資料。但是當多個線程需要使用同一個全局變量的時候。

如上面這個函數:假設tickes=1;當線程1進來發現tickes>0,線程1進入if語句,如果當thread1還沒有進行tickes–,此時線程2進來了,發現tickes還是大于0的,是以他也進來了,這樣就會讓tickes被自減兩次,進而得到-1,一個錯誤的結果。

如何預防上面的情況呢?隻要當某一個線程通路一段代碼片段,其他線程不能通路就行了。

二、通過互斥量解決上面的問題CreateMutex

函數原型:

HNADLE CreateMutex(

LPSECURITY_ATTRIBUTES lpMutexAttributes,

BOOL bInitialOwner,

LPCTSTR lpName

);

第一個參數是安全結構,預設是NULL不能繼承句柄;

第二個參數為FALSE時建立Mutex時不指定所有權,若為TRUE則指定為目前的建立線程ID為所有者,其他線程通路需要先ReleaseMutex;

第三個參數用于設定Mutex名,為NULL時表示匿名互斥量

WaitForSingleObject(); 請求一個互斥量的通路權;

ReleaseMutex();釋放一個互斥量的通路權。

上面函數的改進:

//聲明全局變量

HANDLE hMutex;

//在main函數中,建立互斥變量mutex,不繼承,無線程所有權,匿名

hMutex = CreateMutex(NULL, FALSE, NULL);

//Fun函數的改進如下

DWORD WINAPI FunProc(LPVOID lpParameter)

{

while(true)

{

WaitForSingleObject(hMutex, INFINITE);//如果互斥變量沒有被釋放則一直等待

if(tickets>0)
  {
    cout<<"Thread 1 sell ticket: "<<tickets--<<endl;
  }
  else
    break;
  ReleaseMutex(hMutex);
  }
return 0;
           

}

三、互斥變量的其他應用

利用命名互斥量來保證隻有一個程式執行個體運作。

可以建立一個命名互斥量,當程式要重複運作是,檢查互斥量的傳回值,若為ERROR_ALREADY_EXISTS則表示已經有一個執行個體運作了,直接return即可,在源程式中添加一下代碼。

HANDLE hMutex_1 = CreateMutex(NULL, TRUE, “Tickets”);

if(hMutex_1)

{

if(ERROT_ALREADY_EXISTS == GetLastError())

{

cout<<“Only one instance can run !”<<endl;

system(“pause”);

return 0;

}

}

繼續閱讀