程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> 線程,進程間的通訊和同步原理及實現用例和應用

線程,進程間的通訊和同步原理及實現用例和應用

編輯:關於VC++

線程/進程間的通訊方式

—使用全局變量/共享內存

—使用thread中的lParam參數

—使用socket

—使用窗口和消息

—使用命名管道/匿名管道

—使用cmd參數

—使用environment變量

線程的啟動,退出和lParam參數通訊

VC:  
      
#include <windows.h>  
      
DWORD WINAPI ThreadProc(LPVOID lParam);  
      
DWORD dwThreadId;  
HANDLE hThread = CreateThread(NULL, 0, ThreadProc, lParam, 0, &dwThreadId);  
      
::TerminateThread(hThread, 0) //殺死線程,強烈不推薦使用!  
::WaitForSingleObject(hThread, INFINITE) //等待線程退出  
::CloseHandle(hThread); //不再關心該線程  
::GetExitCodeThread (); //獲取線程退出代碼
.Net:  
      
Using System.Threading;  
      
Static void ThreadProc(Object lParam)  
      
Object lParam = null;  
Thread th = new Thread(new ParameterizedThreadStart(ThreadProc));  
th.Start(lParam);  
      
th.IsBackground = true; // 主循環結束後依靠.Net運行機制自動取消該線程  
th.Join(); //等待線程退出  
th.Abort(); //殺死線程

進程的啟動,退出,命令行和環境變量

VC:  
      
#include <windows.h>  
      
STARTUPINFO si = sizeof(STARTUPINFO) };  
PROCESS_INFORMATION ps;  
      
Char* pFileName = …;  
Char* pArgs = …;  
LPVOID pEnv = NULL;  
BOOL bRet = CreateProcess(pFileName, pArgs, …, pEnv, NULL, &si, &ps);  
      
::TerminateProcess(ps.hProcess, 0) //殺死進程,不推薦使用,但比TerminateThread強很多。  
::WaitForSingleObject(ps.hProcess, INFINITE) //等待進程退出  
::CloseHandle(ps.hProcess); // 不再關心該進程  
::GetExitCodeProcess(); //獲取進程退出代碼
.Net:  
      
Using System.Diagnotics;  
      
Process p = new Process();  
p.StartInfo = new ProcessStartInfo();  
p.StartInfo.FileName = …;  
p.StartInfo.Arguments = …;  
p.StartInfo.EnvironmentVariables = …;  
p.Start();  
      
p.WaitForExit(); //等待進程退出  
p.Kill(); //殺死進程  
p.ExitCode // 獲取進程退出代碼

管道通訊

創建匿名管道(讀管道和寫管道)  
BOOL WINAPI CreatePipe(  
        PHANDLE hReadPipe,    
        PHANDLE hWritePipe,   
        LPSECURITY_ATTRIBUTES lpPipeAttributes,  
        DWORD nSize);   
      
銷毀匿名管道  
BOOL WINAPI CloseHandle(  
        HANDLE hPipe  
    );  
      
管道通訊用例NPSample

共享內存(VC)

CreateFileMapping  
MapViewOfFile  
UnmapViewOfFile  
CloseHandle  
共享內存實現用例ShmSample

線程/進程間的同步

有了以上進程通訊的方式,必然會產生同步問題。  
同步的幾種方式:  
    臨界區CriticalSection --- 輕量級代碼關鍵段  
    互斥鎖Mutex --- 互斥鎖,只能被一個進程擁有  
    信號量Semaphore  --- 信號燈,  
   事件Event    ---- 一次性手動或自動事件  
   原子變量Atomic   ---- 保證一個變量值唯一性  
   自旋鎖SpinLock  ---- 用戶態自旋鎖,適合短代碼加鎖  
         
   都可以跨進程使用,但臨界區跨進程必須放於共享內存中。

CriticalSection

VC:  
      
#include <windows.h>  
      
CRITICAL_SECTION sec;  
      
InitializeCriticalSection(&sec); //初始化臨界區  
InitializeCriticalSectionAndSpinCount(&sec, 2000); //自旋方式初始化臨界區  
DeleteCriticalSection(&sec); //刪除臨界區  
EnterCritcalSection(&sec); //進入臨界區  
LeaveCriticalSection(&sec); //離開臨界區  
TryEnterCriticalSection(&sec); //試圖進入臨界區,進入成功要離開,否則返回FALSE  
      
注意:盡量使用類鎖(自動析構)如有性能或者防止死鎖需要,盡量不使用return,防止離開時忘記釋放鎖
.Net:  
      
String s;  
lock (s) // 鎖定對象s  
{  
    ….;  
}  
      
System.Threading.Motitor.Enter(s);  
      
System.Threading.Motitor.Leave(s); //盡量加到finally塊裡面,避免拋出異常導致鎖未釋放

Mutex

VC:  
      
#include <windows.h>  
      
HANDLE mutex;  
      
mutex = CreateMutex(NULL,…); //初始化匿名互斥鎖  
Mutex = CreateMutex(“123”, …); //初始化有名稱互斥鎖  
Mutex = OpenMutex(“123”, …); //打開有名稱互斥鎖  
CloseHandle(mutex); //關閉互斥鎖;  
WaitForSingleObject(mutex, waitTime); // 等待互斥鎖,第二個參數為等待時間  
ReleaseMutex(mutex); //釋放互斥鎖;  
注意:盡量使用類鎖(自動析構)如有性能或者防止死鎖需要,盡量不使用return,防止離開時忘記釋放鎖  
注意:線程/進程退出時互斥鎖將自動被釋放,其他等待的線程/進程將獲得該鎖並返回WAIT_ABANDONED.
.Net:  
      
Using System.Threading;  
      
Mutex m = new Mutex();  
Mutex m = new Mutex(“123”, …); //初始化有名稱互斥鎖  
m.WaitOne(); // 等待互斥鎖  
m.ReleaseMutex(); // 釋放互斥鎖,盡量加到finally塊裡面,避免拋出異常導致鎖未釋放

Semaphore

VC:  
      
#include <windows.h>  
      
HANDLE sem;  
      
sem = CreateSemaphore(NULL,…); //初始化匿名信號量並初始化初始信號數量  
sem = CreateSemaphore(“123”, …); //初始化有名稱信號量  
sem = OpenSemaphore(“123”, …); //打開有名稱信號量  
CloseHandle(sem); //關閉信號量;  
WaitForSingleObject(sem, waitTime); // 等待信號量,第二個參數為等待時間,若成功,信號量計數-1  
ReleaseSemaphore(sem, count); //將信號量計數增加count;  
注意:盡量使用類鎖(自動析構)如有性能或者防止死鎖需要,盡量不使用return,防止離開時忘記釋放鎖  
注意:線程/進程退出時信號量將不會被釋放,其他等待的線程/進程將依然會鎖住
.Net:  
      
Using System.Threading;  
      
Semaphore m = new Semaphore();  
Semaphore m = new Semaphore(“123”, …); //初始化有名稱信號量  
m.WaitOne(); // 等待信號量並將信號量數目-1  
//
		

.Net:  
      
Using System.Threading;  
      
EventHandle m = new EventHandle ();  
EventHandle m = new EventHandle (“123”, …); //初始化有名稱信號量  
m.WaitOne(); // 等待信號量並將信號量數目-1  
m.Release (count); // 信號量計數增加count,盡量加到finally塊裡面,避免拋出異常導致鎖未釋放

利用Event控制線程運行(VC)

看過很多線程代碼是這樣寫的  
DWORD WINAPI ThreadProc(LPVOID param)  
{  
    while (m_bRun)  
    {  
        DoWork(…);  
        Sleep(1);  
       }  
}  
Void Stop()  
{  
    m_bRun = FALSE;  
    ::WaitForSingleObject(…);  
}
實際應該這樣優化:  
DWORD WINAPI ThreadProc(LPVOID param)  
{  
    while (TRUE)  
    {  
    DWORD dwRet = WaitForSingleObject(hEvent,   1);  
    if (dwRet == WAIT_OBJECT_0)  
        break;  
    DoWork(…);  
    Sleep(1);  
       }  
}  
      
Void Stop()  
{  
    SetEvent(hEvent);  
    ::WaitForSingleObject(…);  
}

原子變量

—原子變量的原理

原子變量的原理是利用硬件支持鎖定某塊內存的功能實現,就算有多個CPU同時訪問該段內存,也只有一個能進入該內存,其他CPU將被鎖住。

由於原子變量並非對代碼段加鎖,而是對數據區加鎖,並且鎖的空間很小,因此一般只適合數量上的(引用計數)或者數值上的(個數,次數)的加鎖。

VC: InterlockedIncrement, InterlockedExchangeAdd, InterlockedDecrement, …

.Net: System.Threading.Interlocked類

自旋鎖

—自旋鎖是用戶態鎖,利用鎖定某塊內存的方式不斷讀取該塊內存數據來加鎖/解鎖,工作機理和原子變量類似,但要注意自旋鎖僅適合非單核CPU(單核在用戶態自旋是沒有意義的)和較短代碼段的鎖,若鎖的時間過長將引起大量的CPU耗損。

同步與死鎖

—死鎖原因

—忘記在某個地方釋放鎖

—使用TerminateThread/TerminateProcess導致鎖對象未釋放

—加鎖未按順序加鎖

—鎖太多,不知該如何加鎖

—每個鎖的鎖周期要短,不要對非關鍵代碼區域段加鎖

—每個鎖的目的要明確,不要一個鎖去鎖太多的對象和元素

—加鎖要按順序加鎖

—注意SendMessage類函數和回調函數的加鎖,確保在調用之前已經釋放了應該釋放的鎖

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved