windows下多執行緒同步(利用事件物件,互斥物件,關鍵程式碼段)實現
一:利用事件實現執行緒同步
1.createthread函式的用法
hThread = CreateThread(&security_attributes, dwStackSize, ThreadProc,pParam, dwFlags, &idThread) ;
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
DWORD dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
第一個引數是指向SECURITY_ATTRIBUTES型態的結構的指標。在Windows 98中忽略該引數。在Windows NT中,它被設為NULL。
第二個引數是用於新執行緒的初始堆疊大小,預設值為0。在任何情況下,Windows根據需要動態延長堆疊的大小。
第三個引數是指向執行緒函式的指標。函式名稱沒有限制,但是必須以下列形式宣告:
DWORD WINAPI ThreadProc (LPVOID pParam) ;
第四個引數為傳遞給ThreadProc的引數。這樣主執行緒和從屬執行緒就可以共享資料,這個引數既可以是數值,也可以是指向其他資訊的指標。
第五個引數通常為0,表示建立之後立刻執行,但當建立的執行緒不馬上執行時為可設定為CREATE_SUSPENDED。執行緒將暫停直到呼叫ResumeThread來恢復執行緒的執行為止。
第六個引數時一個返回值,它用來接收執行緒的ID,可以為該引數傳遞NULL,表明我們對該執行緒的ID不感興趣。
函式返回值為新執行緒的控制代碼。
2.CreateEvent函式
HANDLECreateEvent(
LPSECURITY_ATTRIBUTESlpEventAttributes,// 安全屬性
BOOLbManualReset,// 復位方式
BOOLbInitialState,// 初始狀態
LPCTSTRlpName // 物件名稱
);
lpEventAttributes[輸入]
一個指向SECURITY_ATTRIBUTES結構的指標,確定返回的控制代碼是否可被子程序繼承。如果lpEventAttributes是NULL,此控制代碼不能被繼承。
Windows NT/2000:lpEventAttributes的結構中的成員為新的事件指定了一個安全符。如果lpEventAttributes是NULL,事件將獲得一個預設的安全符。
bManualReset[輸入]
指定將事件物件建立成手動復原還是自動復原。如果是TRUE,那麼必須用ResetEvent函式來手工將事件的狀態復原到無訊號狀態。如果設定為FALSE,當一個等待執行緒被釋放以後,系統將會自動將事件狀態復原為無訊號狀態。
bInitialState[輸入]
指定事件物件的初始狀態。如果為TRUE,初始狀態為有訊號狀態;否則為無訊號狀態。
lpName[輸入]
指定事件的物件的名稱,是一個以0結束的字串指標。名稱的字元格式限定在MAX_PATH之內。名字是對大小寫敏感的。
如果lpName指定的名字,與一個存在的命名的事件物件的名稱相同,函式將請求EVENT_ALL_ACCESS來訪問存在的物件。這時候,由於bManualReset和bInitialState引數已經在建立事件的程序中設定,這兩個引數將被忽略。如果lpEventAttributes是引數不是NULL,它將確定此控制代碼是否可以被繼承,但是其安全描述符成員將被忽略。
如果lpName為NULL,將建立一個無名的事件物件。
如果lpName的和一個存在的訊號、互斥、等待計時器、作業或者是檔案對映物件名稱相同,函式將會失敗,在GetLastError函式中將返回ERROR_INVALID_HANDLE。造成這種現象的原因是這些物件共享同一個名稱空間。
終端服務(Terminal Services):名稱中可以加入"Global\"或是"Local\"的字首,這樣可以明確的將物件建立在全域性的或事務的名稱空間。名稱的其它部分除了反斜槓(\),可以使用任意字元。詳細內容可參考Kernel Object Name Spaces。
Windows 2000:在Windows 2000系統中,沒有終端服務執行,"Global\"和"Local\"字首將被忽略。名稱的其它部分除了反斜槓(\),可以使用任意字元。
Windows NT 4.0以及早期版本,Windows 95/98:名稱中除了反斜槓(\),可以使用任意字元。
// MultiThread.cpp : 定義控制檯應用程式的入口點。
程式碼一:執行緒不同步的例子(基於事件)
#include "stdafx.h"
#include "stdio.h"
#include "windows.h"
DWORD WINAPI Fun1Proc(LPVOID lpParameter);
DWORD WINAPI Fun2Proc(LPVOID lpParameter);
HANDLE g_hEvent;
int tiket=100;
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE Thread1;
HANDLE Thread2;
g_hEvent=CreateEvent(NULL,FALSE,FALSE,NULL); Thread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
Thread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);
CloseHandle(Thread1);
CloseHandle(Thread2);
SetEvent(g_hEvent);
//Sleep(10);
system("pause");
return 0;
}
DWORD WINAPI Fun1Proc(LPVOID lpParameter)
{
while(1)
{
//Sleep(1);
if(tiket>0)
{
WaitForSingleObject(g_hEvent,INFINITE);
printf("Thread1 sell tiket %d\n",tiket);
tiket--;
SetEvent(g_hEvent);
}
else
{
//SetEvent(g_hEvent);
break;
}
}
return 0;
}
DWORD WINAPI Fun2Proc(LPVOID lpParameter)
{
while(1)
{
//Sleep(1);
WaitForSingleObject(g_hEvent,INFINITE);
if(tiket>0)
{
printf("Thread2 sell tiket %d\n",tiket);
tiket--;
SetEvent(g_hEvent);
}
else
{
//SetEvent(g_hEvent);
break;
}
}
return 0;
}
程式碼二:執行緒同步的例子(基於事件)
#include "stdafx.h"
#include "stdio.h"
#include "windows.h"
DWORD WINAPI Fun1Proc(LPVOID lpParameter);
DWORD WINAPI Fun2Proc(LPVOID lpParameter);
HANDLE g_hEvent;
int tiket=100;
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE Thread1;
HANDLE Thread2;
g_hEvent=CreateEvent(NULL,FALSE,FALSE,NULL); Thread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
Thread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);
CloseHandle(Thread1);
CloseHandle(Thread2);
SetEvent(g_hEvent);
//Sleep(10);
system("pause");
return 0;
}
DWORD WINAPI Fun1Proc(LPVOID lpParameter)
{
while(1)
{
//Sleep(1);
WaitForSingleObject(g_hEvent,INFINITE);
if(tiket>0)
{
printf("Thread1 sell tiket %d\n",tiket);
tiket--;
SetEvent(g_hEvent);
}
else
{
//SetEvent(g_hEvent);
break;
}
}
return 0;
}
DWORD WINAPI Fun2Proc(LPVOID lpParameter)
{
while(1)
{
//Sleep(1);
WaitForSingleObject(g_hEvent,INFINITE);
if(tiket>0)
{
printf("Thread2 sell tiket %d\n",tiket);
tiket--;
SetEvent(g_hEvent);
}
else
{
//SetEvent(g_hEvent);
break;
}
}
return 0;
}
二:利用互斥物件實現執行緒同步
CreateMutex()函式可用來建立一個有名或無名的互斥量物件,該物件是一個核心物件,它能夠確保執行緒對單個資源的互斥訪問權。
其函式原型為:
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTESlpMutexAttributes, // 指向安全屬性的指標
BOOLbInitialOwner, // 初始化互斥物件的所有者
LPCTSTRlpName // 指向互斥物件名的指標
);
返回值:
如執行成功,就返回互斥體物件的控制代碼;零表示出錯。會設定GetLastError。即使返回的是一個有效控制代碼,但倘若指定的名字已經存在,GetLastError也會設為ERROR_ALREADY_EXISTS。
引數表:
lpMutexAttributes SECURITY_ATTRIBUTES,指定一個SECURITY_ATTRIBUTES結構,
可以賦值NULL,表示讓互斥物件擁有預設的安全屬性。
bInitialOwner Long:若為TRUE,則建立該互斥物件的執行緒擁有該互斥物件的所有權,否則,沒有所有權。
lpName String,指定互斥體物件的名字。若為NULL,表示建立一個匿名的互斥物件。
注意:如果執行緒對共享資源的訪問結束後,應該釋放互斥物件的所有權,即將互斥物件置於有訊號的狀態,使用函式
BOOL WIANPI ReleaseMutex(
HANDLE hMutex
);
返回值:函式返回成功,返回非0,否則返回0;
引數:互斥物件的控制代碼。
對於互斥物件遵循誰擁有誰釋放的原則。
程式碼三:
#include "stdafx.h"
#include "stdio.h"
#include "windows.h"
DWORD WINAPI Fun1Proc(LPVOID lpParameter);
DWORD WINAPI Fun2Proc(LPVOID lpParameter);
HANDLE h_Mutex;
int tiket=100;
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE Thread1;
HANDLE Thread2;
h_Mutex=CreateMutex(NULL,FALSE,NULL);
Thread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
Thread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);
CloseHandle(Thread1);
CloseHandle(Thread2);
//Sleep(10);
system("pause");
return 0;
}
DWORD WINAPI Fun1Proc(LPVOID lpParameter)
{
while(1)
{
//Sleep(1);
WaitForSingleObject(h_Mutex,INFINITE);
if(tiket>0)
{
printf("Thread1 sell tiket %d\n",tiket);
tiket--;
ReleaseMutex(h_Mutex);
}
else
{
break;
}
}
return 0;
}
DWORD WINAPI Fun2Proc(LPVOID lpParameter)
{
while(1)
{
//Sleep(1);
WaitForSingleObject(h_Mutex,INFINITE);
if(tiket>0)
{
printf("Thread2 sell tiket %d\n",tiket);
tiket--;
ReleaseMutex(h_Mutex);
}
else
{
break;
}
}
return 0;
}
三:利用關鍵程式碼段(臨界區)實現執行緒同步
關鍵程式碼段,也稱為臨界區,工作在使用者方式下,它是指一小段程式碼,在程式碼能夠執行前,它必須獨佔對某些資源的訪問權。
VOID InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection )
函式功能初始化一個臨界資源物件。該函式無返回值。單程序的各個執行緒可以使用臨界資源物件來解決同步互斥問題,該物件不能保證哪個執行緒能夠獲得到臨界資源物件,該系統能公平的對待每一個執行緒。lpCriticalSection 臨界資源物件指標,該引數是一個out型別。 EnterCriticalSection(__inout LPCRITICAL_SECTION lpCriticalSection);
該函式用以獲得指定的臨界區的物件的所有權,該函式等待臨界區物件的所有權,如果該所有權賦予了呼叫執行緒,則該函式返回,否則該函式會一直等待,從而導致執行緒等待。
LeaveCriticalSection(__inout LPCRITICAL_SECTION lpCriticalSection);
執行緒使用完所保護的資源後,需要呼叫該函式,釋放指定的臨界區物件的所有權,這時候其他想要獲得該臨界區物件所有權的執行緒就可以獲得該所有權,從而進入關鍵程式碼段,訪問保護的資源。
DeleteCriticalSection(__inout LPCRITICAL_SECTION lpCriticalSection);
該函式釋放一個沒有被任何執行緒所擁有的臨界區物件的所有資源。
程式碼四:
#include "stdafx.h"
#include "stdio.h"
#include "windows.h"
DWORD WINAPI Fun1Proc(LPVOID lpParameter);
DWORD WINAPI Fun2Proc(LPVOID lpParameter);
CRITICAL_SECTION g_cs;
int tiket=100;
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE Thread1;
HANDLE Thread2;
Thread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
Thread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);
CloseHandle(Thread1);
CloseHandle(Thread2);
InitializeCriticalSection(&g_cs);
//Sleep(10);
system("pause");
DeleteCriticalSection(&g_cs);
return 0;
}
DWORD WINAPI Fun1Proc(LPVOID lpParameter)
{
while(1)
{
//Sleep(1);
EnterCriticalSection(&g_cs);
if(tiket>0)
{
printf("Thread1 sell tiket %d\n",tiket);
tiket--;
LeaveCriticalSection(&g_cs);
}
else
{
break;
}
}
return 0;
}
DWORD WINAPI Fun2Proc(LPVOID lpParameter)
{
while(1)
{
//Sleep(1);
EnterCriticalSection(&g_cs);
if(tiket>0)
{
printf("Thread2 sell tiket %d\n",tiket);
tiket--;
LeaveCriticalSection(&g_cs);
}
else
{
break;
}
}
return 0;
}
四:執行緒死鎖
解釋:對於多執行緒來說,如果執行緒1擁有了臨界區物件A,等待臨界區物件B的擁有權,執行緒2擁有了臨界區物件B,等待臨界區物件A的所有權,這就造成了死鎖。
程式碼五:執行緒死鎖
#include "stdafx.h"
#include "stdio.h"
#include "windows.h"
DWORD WINAPI Fun1Proc(LPVOID lpParameter);
DWORD WINAPI Fun2Proc(LPVOID lpParameter);
CRITICAL_SECTION g_cs;
CRITICAL_SECTION g_cs1;
int tiket=100;
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE Thread1;
HANDLE Thread2;
Thread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
Thread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);
CloseHandle(Thread1);
CloseHandle(Thread2);
InitializeCriticalSection(&g_cs);
InitializeCriticalSection(&g_cs1);
//Sleep(10);
printf("I am in the main Function!\n");
Sleep(10000);
//system("pause");
DeleteCriticalSection(&g_cs);
DeleteCriticalSection(&g_cs1);
return 0;
}
DWORD WINAPI Fun1Proc(LPVOID lpParameter)
{
while(1)
{
//Sleep(1);
EnterCriticalSection(&g_cs);
Sleep(1);
EnterCriticalSection(&g_cs1);
if(tiket>0)
{
printf("Thread1 sell tiket %d\n",tiket);
tiket--;
LeaveCriticalSection(&g_cs);
LeaveCriticalSection(&g_cs1);
}
else
{
break;
}
}
return 0;
}
DWORD WINAPI Fun2Proc(LPVOID lpParameter)
{
while(1)
{
//Sleep(1);
EnterCriticalSection(&g_cs);
Sleep(1);
EnterCriticalSection(&g_cs1);
if(tiket>0)
{
printf("Thread2 sell tiket %d\n",tiket);
tiket--;
LeaveCriticalSection(&g_cs);
LeaveCriticalSection(&g_cs1);
}
else
{
break;
}
}
return 0;
}
五:總結
互斥物件、事件物件和關鍵程式碼段的比較
1.互斥物件和事件物件都屬於核心物件,利用核心物件進行執行緒同步時,速度較慢,但利用互斥物件和事件物件時,可以在多個程序中的多個執行緒間進行同步。
2.關鍵程式碼段工作在使用者方式下,同步速度較快,但在使用關鍵程式碼段時,很容易進入死鎖狀態,因為在等待進入關鍵程式碼段時無法設定超時值。
相關推薦
windows下多執行緒同步(利用事件物件,互斥物件,關鍵程式碼段)實現
一:利用事件實現執行緒同步 1.createthread函式的用法 hThread = CreateThread(&security_attributes, dwStackSize, ThreadProc,pParam, dwFlags, &idThre
windows下多執行緒同步
互斥量(鎖)適用範圍:可以跨程序同步,還可以用來保證程式只有一個互斥鎖例項執行(建立命名互斥量),也可以用來做執行緒間的同步如果用於程序間同步,一個執行緒建立互斥量物件後,另一個程序只需要獲取互斥量就可以,可以用OpenMutex(MUTEX_ALL_ACCESS,FALSE
linux下多執行緒同步機制之訊號量、互斥量、讀寫鎖、條件變數
之前有寫過類似的部落格,這東西不用老忘,現在又有更清晰的理解了。 一、訊號量 編譯時候加入-lrt 訊號量最基本的兩個操作就是PV操作:P()操作實現訊號量減少,V()操作實現訊號量的增加 訊號量的值取決於訊號量的型別,訊號量的型別有多種: (1)二進位制訊號量:0與1.
c++ 網路程式設計(九)TCP/IP LINUX/windows下 多執行緒超詳細教程 以及 多執行緒實現服務端
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <process.h> #include <winsock2.h> #include <win
c++ 網路程式設計(九)TCP/IP LINUX/windows下 多執行緒超詳細教程 以及 多執行緒實現服務端
原文作者:aircraft 原文連結:https://www.cnblogs.com/DOMLX/p/9661012.html 先講Linux下(windows下在後面可以直接跳到後面看): 一.執行緒基本概念 前面我們講過多程序伺服器,但我們知道它開銷很大
多核、多處理器環境下多執行緒同步技巧
這裡稍微講解一下。 在ThreadProc中,我們的目的仍然是要將counter加1。那麼我們先讀取counter的值,然後將該值加1後的 值儲存到一邊。然後我們就可以用自己打造的cmpxchg_64做原子操作了。如果返回值為1,說明原來的值與counter在比較時的值是完全一樣的。 比如,初始值為0,那麼在
windows下Libevent的多執行緒封裝(以檔案傳輸為例)
1、主執行緒負責監聽,子執行緒負責響應連線,同時每個子執行緒增加了連結串列來管理連線進來的客戶端,將上一節中的記憶體管理類也封裝進去。總體框架是不變的。 2、封裝類的實現: #pragma once #include "winsock2.h" #include "event2/liste
linux 下基於特定通訊協議利用多執行緒同步通訊機制實現的串列埠通訊
</pre><pre name="code" class="cpp">/** *@Title:利用多執行緒同步通訊機制實現串列埠通訊 *@Introduce:主要完成根據特定的通訊協議實現串列埠與PC上特定串列埠 * 通訊軟體的通訊。測試版,只
C#多執行緒基礎(多執行緒的優先順序、狀態、同步)
一、關於多執行緒的優先順序、狀態、同步指令碼如下: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System
Java多執行緒學習(十):AQS 原理以及 AQS 同步元件總結
常見問題:AQS 原理?;CountDownLatch和CyclicBarrier瞭解嗎,兩者的區別是什麼?用過Semaphore嗎? 本節思維導圖: 阿里雲產品 1888 代金券領取:https://promotion.aliyun.com/ntms
C++多執行緒同步技巧(二) ---事件
簡介 Windows線上程控制方面提供了多種訊號處理機制,其中一種便是使用 CreateEvent() 函式建立事件,然後使用訊號控制執行緒執行。其中將事件變為有訊號可使用 SetEvent() 函式,將事件訊號復位(變為無訊號)可使用 ResetEvent(
Java多執行緒程式設計-(5)-使用Lock物件實現同步以及執行緒間通訊
前幾篇: 在《Java多執行緒程式設計-(4)-執行緒間通訊機制的介紹與使用》已經學習了,可以使用方法wait/notify 結合同步關鍵字syn
基本執行緒同步(八)在Lock中使用多個條件
宣告:本文是《 Java 7 Concurrency Cookbook 》的第二章,作者: Javier Fernández González 譯者:許巧輝 校對:方騰飛 在Lock中使用多個條件 一個鎖可能伴隨著多個條件。這些條件宣告在Condition介面中。 這些條件的目的是允許
《瘋狂Java講義(第4版)》-----第16章【多執行緒】(控制執行緒、執行緒同步)
控制執行緒 join執行緒 等那個執行緒做完後,當前執行緒再做! import java.lang.Thread; public class MyThread extends Thread{ public MyThread(String name){ super(
利用synchronized實現多執行緒同步
多執行緒程式設計帶來便利性的同時,也給我們的程式設計帶來了難度,因為多執行緒的執行具有隨機性,當多個執行緒對共享資源操作時,就很容易引發問題。 下面模擬了一個取錢的執行緒,當兩個取錢的執行緒對同一個賬戶進行操作時,我們就會發現異常。 下
Java多執行緒程式設計-(12)-Java中的佇列同步器AQS和ReentrantLock鎖原理簡要分析
原文出自 : https://blog.csdn.net/xlgen157387/article/details/78341626 一、Lock介面 在上一篇文章中: Java多執行緒程式設計-(5)-使用Lock物件實現同步以及執行緒間通訊 介紹
Java多執行緒(十五):多執行緒同步的五種方法
一、為什麼要執行緒同步 因為當我們有多個執行緒要同時訪問一個變數或物件時,如果這些執行緒中既有讀又有寫操作時,就會導致變數值或物件的狀態出現混亂,從而導致程式異常。舉個例子,如果一個銀行賬戶同時被兩個執行緒操作,一個取100塊,一個存錢100塊。假設賬戶原本有0塊,如
多執行緒同步-互斥物件(深入理解Mutex)
多執行緒之執行緒同步Mutex (功能與Critial Sections相同,但是屬於核心物件,訪問速度較慢,可以被不同程序呼叫)一 Mutex 互斥物件(mutex)核心物件能夠確保執行緒擁有對單個資源的互斥訪問權。實際上互斥物件是因此而得名的。互斥物件包含一個使用數量,
多執行緒同步與互斥(3)
在進行多執行緒程式設計時,難免還要碰到兩個問題,那就執行緒間的互斥與同步: 執行緒同步是指執行緒之間所具有的一種制約關係,一個執行緒的執行依賴另一個執行緒的訊息,當它沒有得到另一個執行緒的訊息時應等待,直到訊息到達時才被喚醒。 執行緒互斥是指對於共享的程序系統資源,在各單個執行緒訪問時的排它性。當有若干個執行
利用lock實現多執行緒同步
我們不僅可以使用synchronized來實現多執行緒同步,還可以通過建立鎖物件來實現多執行緒的同步,還是上次模擬取現的操作,這次利用lock物件實現同步,下面是程式碼: import java.util.concurrent.locks.Reentrant