• 方案介紹
  • 附件下載
  • 相關(guān)推薦
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

Windows下線程的競(jìng)爭(zhēng)與資源保護(hù)(win32-API)

5小時(shí)前
136
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

更多詳細(xì)資料請(qǐng)聯(lián)系.docx

共1個(gè)文件

一、前言

在線程編程中,資源共享與保護(hù)是一個(gè)核心議題,尤其當(dāng)多個(gè)線程試圖同時(shí)訪問(wèn)同一份資源時(shí),如果不采取適當(dāng)?shù)拇胧?,就?huì)引發(fā)一系列的問(wèn)題,如數(shù)據(jù)不一致、競(jìng)態(tài)條件、死鎖等。為了確保數(shù)據(jù)的一致性和線程安全,多種資源保護(hù)機(jī)制被設(shè)計(jì)出來(lái),這些機(jī)制主要圍繞著資源的互斥訪問(wèn)展開(kāi),以防止多個(gè)線程同時(shí)修改同一份數(shù)據(jù)而導(dǎo)致的錯(cuò)誤。

臨界區(qū)(Critical Section)

臨界區(qū)是最基本的資源保護(hù)方式之一,它允許同一時(shí)間內(nèi)只有一個(gè)線程進(jìn)入臨界區(qū)并訪問(wèn)受保護(hù)的資源。臨界區(qū)通過(guò)操作系統(tǒng)提供的原語(yǔ)實(shí)現(xiàn),如Windows下的EnterCriticalSectionLeaveCriticalSection函數(shù)。當(dāng)一個(gè)線程進(jìn)入臨界區(qū)時(shí),其他試圖進(jìn)入同一臨界區(qū)的線程將被阻塞,直到當(dāng)前線程離開(kāi)臨界區(qū)。臨界區(qū)適用于同一進(jìn)程內(nèi)的線程,因?yàn)樗鼈児蚕硐嗤牡刂房臻g,可以快速且有效地進(jìn)行同步。

互斥量(Mutex)

互斥量是一種更通用的同步機(jī)制,它不僅限于同一進(jìn)程內(nèi)的線程,還可以跨越進(jìn)程邊界。互斥量提供了比臨界區(qū)更強(qiáng)大的功能,如命名互斥量,這允許不同進(jìn)程中的線程可以共享同一個(gè)互斥量對(duì)象?;コ饬客ㄟ^(guò)CreateMutex函數(shù)創(chuàng)建,并使用WaitForSingleObjectReleaseMutex函數(shù)進(jìn)行鎖定和解鎖?;コ饬恐С謨?yōu)先級(jí)繼承,這有助于防止優(yōu)先級(jí)反轉(zhuǎn)問(wèn)題,即高優(yōu)先級(jí)線程等待低優(yōu)先級(jí)線程釋放資源的情況。

信號(hào)量(Semaphore)

信號(hào)量用于控制對(duì)有限數(shù)量資源的訪問(wèn),例如控制并發(fā)訪問(wèn)數(shù)據(jù)庫(kù)連接的數(shù)量。信號(hào)量維護(hù)一個(gè)計(jì)數(shù)器,當(dāng)計(jì)數(shù)器大于零時(shí),線程可以獲取信號(hào)量并減少計(jì)數(shù)器的值,從而獲得訪問(wèn)資源的許可。當(dāng)線程釋放信號(hào)量時(shí),計(jì)數(shù)器增加,允許其他等待的線程獲取信號(hào)量。信號(hào)量分為二進(jìn)制信號(hào)量和計(jì)數(shù)信號(hào)量,前者只能在0和1之間切換,常用于實(shí)現(xiàn)互斥訪問(wèn);后者可以有任意非負(fù)值,用于控制資源的數(shù)量。

自旋鎖(Spin Lock)

自旋鎖是一種非阻塞的同步機(jī)制,主要用于短時(shí)間的鎖定,尤其是在高負(fù)載、高頻率的訪問(wèn)場(chǎng)景中。當(dāng)一個(gè)線程嘗試獲取一個(gè)已被占用的自旋鎖時(shí),它不會(huì)被阻塞,而是循環(huán)檢查鎖的狀態(tài),直到鎖被釋放。自旋鎖避免了線程上下文切換帶來(lái)的開(kāi)銷(xiāo),但在鎖長(zhǎng)時(shí)間被占用的情況下,可能會(huì)消耗大量的CPU資源。

讀寫(xiě)鎖(Reader-Writer Lock)

讀寫(xiě)鎖允許多個(gè)讀線程同時(shí)訪問(wèn)資源,但只允許一個(gè)寫(xiě)線程訪問(wèn)資源,這在讀操作遠(yuǎn)多于寫(xiě)操作的場(chǎng)景中非常有效。讀寫(xiě)鎖優(yōu)化了讀取性能,因?yàn)槎鄠€(gè)讀線程可以同時(shí)持有讀鎖,而寫(xiě)操作則需要獨(dú)占鎖才能進(jìn)行,以防止數(shù)據(jù)的不一致性。

條件變量(Condition Variable)

條件變量通常與互斥量結(jié)合使用,用于實(shí)現(xiàn)線程間的高級(jí)同步。當(dāng)線程需要等待某個(gè)條件變?yōu)檎鏁r(shí),它可以釋放互斥量并調(diào)用條件變量的Wait函數(shù)。當(dāng)條件滿足時(shí),線程可以被喚醒并重新獲取互斥量,繼續(xù)執(zhí)行。條件變量是實(shí)現(xiàn)生產(chǎn)者-消費(fèi)者模式、讀者-寫(xiě)者模式等復(fù)雜同步策略的基礎(chǔ)。

多線程編程中,正確選擇和使用這些同步機(jī)制對(duì)于保證程序的正確性和性能至關(guān)重要。開(kāi)發(fā)人員必須仔細(xì)分析線程間的交互,識(shí)別出可能引起競(jìng)態(tài)條件的資源,并采取適當(dāng)?shù)谋Wo(hù)措施,以確保數(shù)據(jù)的一致性和線程的安全運(yùn)行。同時(shí),過(guò)度的同步也可能導(dǎo)致性能瓶頸,因此在設(shè)計(jì)時(shí)還需平衡同步的必要性和程序的效率。

二、實(shí)操代碼

2.1 互斥量案例-消費(fèi)者與生產(chǎn)者模型

開(kāi)發(fā)環(huán)境:在Windows下安裝一個(gè)VS即可。我當(dāng)前采用的版本是VS2020。

創(chuàng)建一個(gè)基于互斥量(mutex)的火車(chē)票售賣(mài)模型,可以很好地展示消費(fèi)者與生產(chǎn)者關(guān)系中資源保護(hù)的重要性。在這個(gè)模型中,“生產(chǎn)者”可以視為負(fù)責(zé)初始化火車(chē)票數(shù)量的角色,而“消費(fèi)者”則是購(gòu)買(mǎi)火車(chē)票的線程。為了確保在多線程環(huán)境中票數(shù)的正確性和一致性,需要使用互斥量來(lái)保護(hù)對(duì)票數(shù)的訪問(wèn)和修改。

下面是一個(gè)使用C語(yǔ)言和Windows API實(shí)現(xiàn)的火車(chē)票售賣(mài)模型的示例代碼:

#include <windows.h>
#include <stdio.h>

#define TICKET_COUNT 10

// 定義互斥量
CRITICAL_SECTION ticketMutex;
int ticketsAvailable = TICKET_COUNT;

// 消費(fèi)者線程函數(shù)
DWORD WINAPI ConsumerThread(LPVOID lpParameter)
{
    int id = (int)lpParameter;
    while (ticketsAvailable > 0)
    {
        // 進(jìn)入臨界區(qū)
        EnterCriticalSection(&ticketMutex);

        if (ticketsAvailable > 0)
        {
            ticketsAvailable--;
            printf("Consumer %d bought a ticket. Tickets left: %dn", id, ticketsAvailable);
        }

        // 離開(kāi)臨界區(qū)
        LeaveCriticalSection(&ticketMutex);
    }

    return 0;
}

int main()
{
    HANDLE consumerThreads[TICKET_COUNT * 2]; // 假設(shè)有兩倍于票數(shù)的消費(fèi)者
    DWORD threadIDs[TICKET_COUNT * 2];

    // 初始化臨界區(qū)
    InitializeCriticalSection(&ticketMutex);

    // 創(chuàng)建消費(fèi)者線程
    for (int i = 0; i < TICKET_COUNT * 2; i++)
    {
        consumerThreads[i] = CreateThread(
            NULL,                    // 默認(rèn)安全屬性
            0,                       // 使用默認(rèn)堆棧大小
            ConsumerThread,          // 線程函數(shù)
            (LPVOID)(i + 1),         // 傳遞給線程函數(shù)的參數(shù)
            0,                       // 創(chuàng)建標(biāo)志,0表示立即啟動(dòng)
            &threadIDs[i]);          // 返回線程ID
        if (consumerThreads[i] == NULL)
        {
            printf("Failed to create thread %d.n", i);
            return 1;
        }
    }

    // 等待所有線程結(jié)束
    for (int i = 0; i < TICKET_COUNT * 2; i++)
    {
        WaitForSingleObject(consumerThreads[i], INFINITE);
    }

    // 刪除臨界區(qū)
    DeleteCriticalSection(&ticketMutex);

    // 關(guān)閉所有線程句柄
    for (int i = 0; i < TICKET_COUNT * 2; i++)
    {
        CloseHandle(consumerThreads[i]);
    }

    return 0;
}

在這個(gè)示例中,定義了一個(gè)CRITICAL_SECTION類(lèi)型的ticketMutex互斥量來(lái)保護(hù)對(duì)ticketsAvailable變量的訪問(wèn)。在ConsumerThread函數(shù)中,每個(gè)線程在嘗試購(gòu)買(mǎi)一張票之前,都需要先通過(guò)EnterCriticalSection函數(shù)進(jìn)入臨界區(qū),以確保在任何時(shí)刻只有一個(gè)線程可以修改票數(shù)。購(gòu)買(mǎi)完成后,通過(guò)LeaveCriticalSection函數(shù)離開(kāi)臨界區(qū),允許其他線程有機(jī)會(huì)進(jìn)入臨界區(qū)并嘗試購(gòu)票。

雖然創(chuàng)建了兩倍于票數(shù)的消費(fèi)者線程,但由于互斥量的存在,最多只會(huì)有一張票在同一時(shí)刻被售出,從而避免了資源競(jìng)爭(zhēng)和數(shù)據(jù)不一致的問(wèn)題。

此代碼演示了如何在多線程環(huán)境中使用互斥量來(lái)保護(hù)共享資源,確保數(shù)據(jù)的一致性和線程安全。在實(shí)際應(yīng)用中,互斥量是處理多線程并發(fā)訪問(wèn)問(wèn)題的重要工具,尤其是在涉及到資源有限且需要嚴(yán)格控制訪問(wèn)順序的場(chǎng)景下。

2.2 使用臨界區(qū)保護(hù)共享資源

開(kāi)發(fā)環(huán)境:在Windows下安裝一個(gè)VS即可。我當(dāng)前采用的版本是VS2020。

使用臨界區(qū)(Critical Section)來(lái)保護(hù)共享資源,如火車(chē)票數(shù)量,在多線程環(huán)境中確保數(shù)據(jù)一致性。

下面是一個(gè)使用C語(yǔ)言和Windows API實(shí)現(xiàn)的火車(chē)票售賣(mài)模型,其中包含了生產(chǎn)者初始化票數(shù)和多個(gè)消費(fèi)者線程購(gòu)買(mǎi)票的過(guò)程。這個(gè)模型將展示如何使用臨界區(qū)來(lái)避免競(jìng)態(tài)條件,確保所有線程安全地訪問(wèn)和修改票數(shù)。

#include <windows.h>
#include <stdio.h>

#define TICKET_COUNT 10

// 定義臨界區(qū)
CRITICAL_SECTION ticketMutex;
int ticketsAvailable = TICKET_COUNT;

// 消費(fèi)者線程函數(shù)
DWORD WINAPI ConsumerThread(LPVOID lpParameter)
{
    int id = (int)lpParameter;

    while (1)
    {
        // 進(jìn)入臨界區(qū)
        EnterCriticalSection(&ticketMutex);

        // 檢查是否有票
        if (ticketsAvailable > 0)
        {
            ticketsAvailable--;
            printf("Consumer %d bought a ticket. Tickets left: %dn", id, ticketsAvailable);
        }
        else
        {
            // 如果沒(méi)有票了,退出循環(huán)
            LeaveCriticalSection(&ticketMutex);
            break;
        }

        // 離開(kāi)臨界區(qū)
        LeaveCriticalSection(&ticketMutex);
    }

    return 0;
}

int main()
{
    HANDLE consumerThreads[TICKET_COUNT * 2]; // 假設(shè)有兩倍于票數(shù)的消費(fèi)者
    DWORD threadIDs[TICKET_COUNT * 2];

    // 初始化臨界區(qū)
    InitializeCriticalSection(&ticketMutex);

    // 創(chuàng)建消費(fèi)者線程
    for (int i = 0; i < TICKET_COUNT * 2; i++)
    {
        consumerThreads[i] = CreateThread(
            NULL,                    // 默認(rèn)安全屬性
            0,                       // 使用默認(rèn)堆棧大小
            ConsumerThread,          // 線程函數(shù)
            (LPVOID)(i + 1),         // 傳遞給線程函數(shù)的參數(shù)
            0,                       // 創(chuàng)建標(biāo)志,0表示立即啟動(dòng)
            &threadIDs[i]);          // 返回線程ID
        if (consumerThreads[i] == NULL)
        {
            printf("Failed to create thread %d.n", i);
            return 1;
        }
    }

    // 等待所有線程結(jié)束
    for (int i = 0; i < TICKET_COUNT * 2; i++)
    {
        WaitForSingleObject(consumerThreads[i], INFINITE);
    }

    // 刪除臨界區(qū)
    DeleteCriticalSection(&ticketMutex);

    // 關(guān)閉所有線程句柄
    for (int i = 0; i < TICKET_COUNT * 2; i++)
    {
        CloseHandle(consumerThreads[i]);
    }

    return 0;
}

在這個(gè)代碼示例中,使用InitializeCriticalSection函數(shù)初始化臨界區(qū)ticketMutex,并在每個(gè)線程的ConsumerThread函數(shù)中使用EnterCriticalSectionLeaveCriticalSection函數(shù)來(lái)保護(hù)對(duì)ticketsAvailable變量的訪問(wèn)。這意味著在任何時(shí)候,只有一個(gè)線程能夠修改ticketsAvailable的值,從而避免了多線程并發(fā)訪問(wèn)時(shí)可能出現(xiàn)的數(shù)據(jù)不一致問(wèn)題。

每個(gè)線程在進(jìn)入臨界區(qū)檢查是否有剩余票之前,都要調(diào)用EnterCriticalSection,而在完成票的購(gòu)買(mǎi)之后,調(diào)用LeaveCriticalSection來(lái)釋放臨界區(qū),允許其他線程有機(jī)會(huì)進(jìn)入并購(gòu)買(mǎi)票。當(dāng)票賣(mài)完后,線程會(huì)退出循環(huán)并結(jié)束。

通過(guò)這種方式,臨界區(qū)確保了即使在高并發(fā)的環(huán)境中,火車(chē)票的銷(xiāo)售過(guò)程也能有序進(jìn)行,每張票只被出售一次,且所有消費(fèi)者線程都能正確地跟蹤剩余票數(shù)。

  • 更多詳細(xì)資料請(qǐng)聯(lián)系.docx
    下載

相關(guān)推薦