最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Linux線程互斥之線程加鎖的使用詳解

 更新時間:2025年04月29日 09:54:44   作者:s_little_monster_  
這篇文章主要介紹了Linux線程互斥之線程加鎖的使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教

一、鎖的定義

線程加鎖是在多線程編程環(huán)境中,為了確保在同一時刻只有一個線程能夠訪問特定的共享資源或執(zhí)行特定的代碼段,而采取的一種同步手段,通過在需要保護的資源或代碼段前獲取鎖,在訪問完成后釋放鎖,來實現對共享資源的互斥訪問

二、庫函數

1、初始化互斥鎖

#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
  • 返回值:成功返回0,失敗返回非零錯誤碼
  • mutex:表示要初始化的互斥鎖,pthread_mutex_t是POSIX線程庫中定義的互斥鎖類型
  • attr:包含互斥鎖的屬性,設置為NULL表示使用默認屬性

2、銷毀互斥鎖

#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mutex);
  • 返回值:成功返回0,失敗返回非零錯誤碼
  • mutex:表示要銷毀的互斥鎖

3、加鎖

#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
  • 返回值:成功返回0,失敗返回非零錯誤碼
  • mutex:表示要加鎖的互斥鎖

4、解鎖

#include <pthread.h>
int pthread_mutex_unlock(pthread_mutex_t *mutex);
  • 返回值:成功返回0,失敗返回非零錯誤碼
  • mutex:表示要解鎖的互斥鎖

5、示例

#include <iostream>
#include <pthread.h>
#include <vector>
#include <cstdio>
#include <unistd.h>

using namespace std;

//定義一個全局鎖就可以不需要初始化和銷毀鎖的函數了
//pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

#define NUM 4
//共500張票
int tickets = 500;

class ThreadInfo
{
public:
    ThreadInfo(const string &threadname, pthread_mutex_t *lock)
    :threadname_(threadname)
    ,lock_(lock)
    {}

public:
    string threadname_;
    pthread_mutex_t *lock_;
};

void *GrabTickets(void *args)
{
    ThreadInfo *ti = static_cast<ThreadInfo*>(args);
    string name(ti->threadname_);
    while(true)
    {
        pthread_mutex_lock(ti->lock_); // 加鎖
        if(tickets > 0)
        {
            usleep(10000);
            printf("%s get a ticket: %d\n", name.c_str(), tickets);
            tickets--;
            pthread_mutex_unlock(ti->lock_); // 解鎖
        }
        else 
        {
            pthread_mutex_unlock(ti->lock_); // 解鎖
            break;
        }
        //這里上面的代碼
        usleep(13); // 用休眠來模擬搶到票的后續(xù)動作
    }
    printf("%s quit...\n", name.c_str());
}

int main()
{
    pthread_mutex_t lock; // 定義互斥鎖
    pthread_mutex_init(&lock, nullptr); // 初始化互斥鎖
    vector<pthread_t> tids;
    vector<ThreadInfo*> tis;
    for(int i = 1; i <= NUM; i++)
    {
        pthread_t tid;
        ThreadInfo *ti = new ThreadInfo("Thread-"+to_string(i), &lock);
        pthread_create(&tid, nullptr, GrabTickets, ti);
        tids.push_back(tid);
        tis.push_back(ti);
    }

    // 等待所有線程
    for(auto tid : tids)
    {
        pthread_join(tid, nullptr);
    }
    // 釋放資源
    for(auto ti : tis)
    {
        delete ti;
    }
	// 銷毀互斥鎖
    pthread_mutex_destroy(&lock); 
    
    return 0;
}

這樣就不會出現好多線程搶到一張票或者搶到不存在的票的問題了

三、深入理解鎖

1、解讀鎖的機制

(一)先入為主原則

我們將上方代碼中表示搶到票后續(xù)動作的休眠代碼注釋掉再次執(zhí)行程序我們會發(fā)現,都是線程1搶的票,多次執(zhí)行代碼之后發(fā)現這是概率性問題,但是在搶票的時候,有一段時間的票都是一個線程搶到的,我們預想的應該是幾乎平均分配的樣子

這說明了幾個問題:

  • 第一,線程對于鎖的競爭能力不同,一定有一個首先搶到鎖的線程
  • 第二,一般來說,剛解鎖再去搶鎖的更容易一些,類似于上面的結果,一直是線程1在搶票

(二)鎖和線程

  • 對于上面第二個問題來說,我們有處理方法,這種方法就是同步,同步可以讓所有的線程按照一定的順序獲取鎖
  • 對于其他線程來講,一個線程要么獲取到了鎖,要么釋放了鎖,當前進程訪問臨界區(qū)的過程對于其他線程是原子的

在加鎖期間,即解鎖之前,是可以發(fā)生線程切換的,線程切換的時候是拿著鎖走的,被鎖起來的內容其他線程也是訪問不到臨界區(qū)的的,在該線程再次切換回來的時候,恢復線程上下文繼續(xù)訪問臨界區(qū)代碼

(三)鎖的特點

加鎖的本質就是用時間來換取安全,我們知道在加鎖后,臨界區(qū)的代碼只能由一個線程執(zhí)行,如果是并發(fā)執(zhí)行,至少時間要縮短5倍,但是鎖給我們消除了安全隱患,即可能出現的++、--的隱患

加鎖的表現就是線程對于臨界區(qū)代碼串行執(zhí)行,一條線從上到下

我們加鎖的原則就是盡量保證臨界區(qū)的代碼要少一些,可以使單線程執(zhí)行的代碼量更小,多線程綜合處理的代碼量更大,提高效率

鎖的本身是共享資源,所以加鎖和解鎖本身就被設計成為了原子性操作(加鎖和解鎖通過硬件提供的原子指令,結合操作系統(tǒng)內核態(tài)的底層同步原語支持以及庫層面的合理封裝,來確保操作的原子性),這樣可以確保在多線程環(huán)境下對共享資源加鎖和解鎖操作的完整性與一致性,避免因多線程并發(fā)干擾導致鎖狀態(tài)異常,進而保障線程安全和數據的正確性

2、鎖的原理

下面來看一下加鎖解鎖對應的匯編指令,我們說,一條匯編指令就是原子性的

首先al寄存器中的數字為0時,代表鎖已被拿走,為非零(一般為1)時,代表鎖當前空閑,可以上鎖

加鎖機制

  • movb $0, %al:將值 0 移動到 AL 寄存器
  • xchgb %al, mutex:這是一個原子交換指令,將 AL 寄存器中的值(即 0)與 mutex 變量的值交換
  • if (al寄存器的內容 > 0):檢查 AL 寄存器中的內容(此時它保存的是原來 mutex 的值),如果值大于 0,說明互斥鎖之前沒有被鎖定,鎖定成功,返回 0
  • else:如果 AL 中的值是 0,說明互斥鎖已經被鎖定,程序會等待
  • goto lock:程序跳轉回 lock 標簽,重新嘗試獲取鎖

解鎖機制

  • movb $1, mutex:將值 1 移動到 mutex
  • xchgb %al, mutex:通過交換 AL 中的值和 mutex,實現解鎖
  • return 0:解鎖后,函數返回

四、鎖的封裝

1、LockGuard.hpp

#pragma once
#include <pthread.h>
//簡單的封裝了一下函數,用的時候方便一些
class Mutex
{
public:
    Mutex(pthread_mutex_t *lock)
    :lock_(lock)
    {}

    void Lock()
    {
        pthread_mutex_lock(lock_);
    }

    void Unlock()
    {
        pthread_mutex_unlock(lock_);
    }
private:
    pthread_mutex_t *lock_;
};

class LockGuard
{
public:
    LockGuard(pthread_mutex_t *lock)
    :mutex_(lock)
    {
        mutex_.Lock(); // 對象創(chuàng)建的時候加鎖
    }

    ~LockGuard()
    {
        mutex_.Unlock(); // 對象銷毀的時候解鎖
    }
private:
    Mutex mutex_;
};
#include <iostream>
#include <pthread.h>
#include <vector>
#include <cstdio>
#include <unistd.h>
#include "LockGuard.hpp"

using namespace std;

#define NUM 4

int tickets = 500; 
//全局變量定義鎖
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

class ThreadInfo
{
public:
    ThreadInfo(const string &threadname)
        : threadname_(threadname)

public:
    string threadname_;
};

void *GrabTickets(void *args)
{
    ThreadInfo *ti = static_cast<ThreadInfo *>(args);
    string name(ti->threadname_);
    while (true)
    {
        {
            LockGuard lockguard(&lock); // RAII 風格的鎖
            if (tickets > 0)
            {
                usleep(10000);
                printf("%s get a ticket: %d\n", name.c_str(), tickets);
                tickets--;
            }
            else
            {
                break;
            }
        }
        usleep(13); // 用休眠來模擬搶到票的后續(xù)動作
    }

    printf("%s quit...\n", name.c_str());
}

int main()
{
    vector<pthread_t> tids;
    vector<ThreadInfo *> tis;
    for (int i = 1; i <= NUM; i++)
    {
        pthread_t tid;
        ThreadInfo *ti = new ThreadInfo("Thread-" + to_string(i));
        pthread_create(&tid, nullptr, GrabTickets, ti);
        tids.push_back(tid);
        tis.push_back(ti);
    }

    // 等待所有線程
    for (auto tid : tids)
    {
        pthread_join(tid, nullptr);
    }

    // 釋放資源
    for (auto ti : tis)
    {
        delete ti;
    }

    pthread_mutex_destroy(&lock);
    return 0;
}

這里封裝的鎖是RAII風格的鎖,RAII風格是一種在 C++ 等編程語言中利用對象的構造和析構函數來自動管理資源的技術,確保資源在對象創(chuàng)建時獲取,在對象生命周期結束時自動釋放,以防止資源泄漏并簡化資源管理

總結

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • linux中使用grep命令詳解

    linux中使用grep命令詳解

    在本篇文章里小編給大家整理了關于linux中使用grep命令的相關知識點內容,需要的朋友們跟著學習下。
    2019-03-03
  • linux服務器下通過iptables+Denyhost抵御暴力破解的配置方法

    linux服務器下通過iptables+Denyhost抵御暴力破解的配置方法

    這篇文章主要介紹了linux服務器下通過iptables + Denyhost抵御暴力破解的方法,需要的朋友可以參考下
    2014-11-11
  • 記一次Ubuntu服務器被黑經歷

    記一次Ubuntu服務器被黑經歷

    最近我們的一臺Ubuntu阿里云服務器一直提示有肉雞行為,提示了好幾天,開始并沒有關注,然后連續(xù)幾天后發(fā)現應該是個大問題啊,很可能服務被侵入了。下面通過本文給大家分享下詳情
    2016-11-11
  • CentOS 7 在vmware中的網絡設置詳細介紹

    CentOS 7 在vmware中的網絡設置詳細介紹

    這篇文章主要介紹了CentOS 7 在vmware中的網絡設置詳細介紹的相關資料,這里對主要網絡問題進行了一一介紹及如何解決,需要的朋友可以參考下
    2016-11-11
  • linux設置tomcat開機啟動方式

    linux設置tomcat開機啟動方式

    本文介紹了在Linux系統(tǒng)中配置Tomcat開機自啟動的方法,包括創(chuàng)建systemd服務文件、重新加載系統(tǒng)管理器配置、啟用服務等步驟,并說明了如何指定JDK和配置日志保留期限
    2026-03-03
  • Linux-Redhat系統(tǒng)啟動讀取文件方式

    Linux-Redhat系統(tǒng)啟動讀取文件方式

    文章詳細描述了RHEL系統(tǒng)從啟動到登錄的整個過程,包括讀取關鍵配置文件、加載內核、掛載文件系統(tǒng)、啟動初始化系統(tǒng)以及進入用戶界面的步驟
    2026-02-02
  • 使用CDN之后APACHE日志記錄中IP地址不正確的解決方案

    使用CDN之后APACHE日志記錄中IP地址不正確的解決方案

    這篇文章主要介紹了使用CDN之后APACHE日志記錄中IP地址不正確的解決方案,需要的朋友可以參考下
    2014-12-12
  • Linux中使用Cron定時執(zhí)行SQL任務的實現步驟

    Linux中使用Cron定時執(zhí)行SQL任務的實現步驟

    在Linux系統(tǒng)中,計劃任務(Cron)是一種強大的工具,可以自動執(zhí)行預定的任務,它非常適合定期運行腳本、備份數據、清理臨時文件等一系列重復性任務,本文給大家介紹了如何在Linux中使用Cron定時執(zhí)行SQL任務,需要的朋友可以參考下
    2024-11-11
  • Linux中crontab定時任務不執(zhí)行的原因

    Linux中crontab定時任務不執(zhí)行的原因

    本篇文章主要介紹了Linux中crontab定時任務不執(zhí)行的原因,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-03-03
  • 如何理解與識別Linux中的文件類型

    如何理解與識別Linux中的文件類型

    這篇文章主要給大家介紹了關于如何理解與識別Linux中文件類型的相關資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用Linux系統(tǒng)具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2019-04-04

最新評論

三原县| 葵青区| 繁昌县| 志丹县| 呼伦贝尔市| 南川市| 株洲市| 罗城| 华安县| 龙陵县| 安乡县| 洛阳市| 定兴县| 微博| 犍为县| 谷城县| 延吉市| 白沙| 永善县| 通化市| 托里县| 南华县| 托克托县| 陈巴尔虎旗| 科技| 中宁县| 册亨县| 蚌埠市| 英超| 芜湖市| 桓台县| 美姑县| 鄂伦春自治旗| 蒙山县| 民勤县| 汶上县| 宜春市| 拜城县| 洛宁县| 秀山| 聂拉木县|