国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Linux之 線程池 | 單例模式的線程安全問題 | 其他鎖

這篇具有很好參考價值的文章主要介紹了Linux之 線程池 | 單例模式的線程安全問題 | 其他鎖。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

目錄

一、線程池

1、線程池

2、線程池代碼

3、線程池的應用場景

二、單例模式的線程安全問題

1、線程池的單例模式

2、線程安全問題

三、其他鎖


一、線程池

1、線程池

線程池是一種線程使用模式。線程池里面可以維護一些線程。

為什么要有線程池?

因為在我們使用線程去處理各種任務的時候,尤其是一些執(zhí)行時間短的任務,我們必須要先對線程進行創(chuàng)建然后再進行任務處理,最后再銷毀線程,效率是比較低的。而且有的時候線程過多會帶來調(diào)度開銷,進而影響緩存局部性和整體性能。

于是,我們可以通過線程池預先創(chuàng)建出一批線程,線程池維護著這些線程,線程等待著監(jiān)督管理者分配可并發(fā)執(zhí)行的任務。這避免了在處理短時間任務時創(chuàng)建與銷毀線程的代價。

線程池不僅能夠保證內(nèi)核的充分利用,還能防止過分調(diào)度。

2、線程池代碼

我們先對線程進行封裝:Thread.hpp

#pragma once
#include <iostream>
#include <string>
#include <cstdio>
#include <pthread.h>

using namespace std;
typedef void *(*fun_t)(void *);

class ThreadData
{
public:
    void *arg_;
    string name_;
};

class Thread
{
public:
    Thread(int num, fun_t callback, void *arg)
        : func_(callback)
    {
        char buffer[64];
        snprintf(buffer, sizeof(buffer), "Thread-%d", num);
        name_ = buffer;
        tdata_.name_ = name_;
        tdata_.arg_ = arg;
    }

    void start()
    {
        pthread_create(&tid_, nullptr, func_, (void *)&tdata_);
    }

    void join()
    {
        pthread_join(tid_, nullptr);
    }

    string &name()
    {
        return name_;
    }

    ~Thread()
    {
    }

private:
    pthread_t tid_;
    string name_;
    fun_t func_;
    ThreadData tdata_;
};

線程池代碼:threadPool.hpp:

#pragma once
#include <vector>
#include <queue>
#include "thread.hpp"

#define THREAD_NUM 3

template <class T>
class ThreadPool
{
public:
    bool Empty()
    {
        return task_queue_.empty();
    }

    pthread_mutex_t *getmutex()
    {
        return &lock;
    }

    void wait()
    {
        pthread_cond_wait(&cond, &lock);
    }

    T gettask()
    {
        T t = task_queue_.front();
        task_queue_.pop();
        return t;
    }

public:
    ThreadPool(int num = THREAD_NUM) : num_(num)
    {
        for (int i = 0; i < num_; i++)
        {
            threads_.push_back(new Thread(i, routine, this));
        }
        pthread_mutex_init(&lock, nullptr);
        pthread_cond_init(&cond, nullptr);
    }

    static void *routine(void *arg)
    {
        ThreadData *td = (ThreadData *)arg;
        ThreadPool<T> *tp = (ThreadPool<T> *)td->arg_;
        while (true)
        {
            T task;
            {
                pthread_mutex_lock(tp->getmutex());
                while (tp->Empty())
                    tp->wait();
                task = tp->gettask();
                pthread_mutex_unlock(tp->getmutex());
            }
            cout << "x+y=" << task() << " " << pthread_self() << endl;
        }
    }

    void run()
    {
        for (auto &iter : threads_)
        {
            iter->start();
        }
    }

    void PushTask(const T &task)
    {
        pthread_mutex_lock(&lock);
        task_queue_.push(task);
        pthread_mutex_unlock(&lock);
        pthread_cond_signal(&cond);
    }

    ~ThreadPool()
    {
        for (auto &iter : threads_)
        {
            iter->join();
            delete iter;
        }
        pthread_mutex_destroy(&lock);
        pthread_cond_destroy(&cond);
    }

private:
    vector<Thread *> threads_;
    int num_;
    queue<T> task_queue_;
    pthread_mutex_t lock;
    pthread_cond_t cond;
};

任務:task.hpp:

#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>
#include <unistd.h>

class task
{
public:
    task()
    {
    }
    task(int x, int y)
        : x_(x), y_(y)
    {
    }

    int operator()()
    {
        return x_ + y_;
    }

private:
    int x_;
    int y_;
};

?測試代碼:test.cc:

#include "threadPool.hpp"
#include "task.hpp"
#include <iostream>
#include <ctime>

int main()
{
    srand((unsigned int)time(nullptr) ^ getpid() ^ 12232);
    ThreadPool<task> *tp = new ThreadPool<task>();
    tp->run();
    while (true)
    {
        int x = rand() % 100 + 1;
        sleep(1);
        int y = rand() % 100 + 1;
        task t(x, y);
        tp->PushTask(t);
        cout << x << "+" << y << "=?" << endl;
    }

    return 0;
}

運行結(jié)果:?

Linux之 線程池 | 單例模式的線程安全問題 | 其他鎖,Linux,單例模式,java,開發(fā)語言

3、線程池的應用場景

1、需要大量的線程來完成任務,且完成任務的時間比較短。?
2、對性能要求苛刻的應用,比如要求服務器迅速響應客戶請求。
3、接受突發(fā)性的大量請求,但不至于使服務器因此產(chǎn)生大量線程的應用。突發(fā)性大量客戶請求,在沒有線程池情況下,將產(chǎn)生大量線程,雖然理論上大部分操作系統(tǒng)線程數(shù)目最大值不是問題,短時間內(nèi)產(chǎn)生大量線程可能使內(nèi)存到達極限,出現(xiàn)錯誤。

二、單例模式的線程安全問題

1、線程池的單例模式

首先,我們要做的第一件事就是把構(gòu)造函數(shù)私有,再把拷貝構(gòu)造和賦值運算符重載函數(shù)delete:

private:
    ThreadPool(int num = THREAD_NUM) : num_(num)
    {
        for (int i = 0; i < num_; i++)
        {
            threads_.push_back(new Thread(i, routine, this));
        }
        pthread_mutex_init(&lock, nullptr);
        pthread_cond_init(&cond, nullptr);
    }

    ThreadPool(const TreadPool &other) = delete;
    ThreadPool operator=(const TreadPool &other) = delete;

接下來就要在類中定義一個成員變量:靜態(tài)指針,方便獲取單例對象,并在類外初始化:

//線程池中的成員變量
private:
    vector<Thread *> threads_;
    int num_;
    queue<T> task_queue_;
    pthread_mutex_t lock;
    pthread_cond_t cond;

    static ThreadPool<T> *tp;

//在類外初始化
?template <class T>
ThreadPool<T> *ThreadPool<T>::tp = nullptr;

最后我們寫一個函數(shù)可以獲取單例對象,在設(shè)置獲取單例對象的函數(shù)的時候,注意要設(shè)置成靜態(tài)成員函數(shù),因為在獲取對象前根本沒有對象,無法調(diào)用非靜態(tài)成員函數(shù)(無this指針):?

static ThreadPool<T> *getThreadPool()
{
    if (tp == nullptr)
    {
        tp = new ThreadPool<T>();
    }
    return tp;
}

Linux之 線程池 | 單例模式的線程安全問題 | 其他鎖,Linux,單例模式,java,開發(fā)語言

2、線程安全問題

上面的線程池的單例模式,看起來沒有什么問題。可是當我們有多個線程去調(diào)用 getThreadPool函數(shù),去創(chuàng)建線程池的時候,可能會有多個線程同時進入判斷,判斷出線程池指針為空,然后創(chuàng)建線程池對象。這樣就會創(chuàng)建出多個線程池對象,這就不符合我們單例模式的要求了,所以我們必須讓在同一時刻只有一個線程能夠進入判斷,我們就要用到鎖了。

定義一個靜態(tài)鎖,并初始化:

private:
    vector<Thread *> threads_;
    int num_;
    queue<T> task_queue_;
    pthread_mutex_t lock;
    pthread_cond_t cond;
    static ThreadPool<T> *tp;
    static pthread_mutex_t lock;

// 類外初始化
?template <class T>
pthread_mutex_t ThreadPool<T>::lock = PTHREAD_MUTEX_INITIALIZER;

對 getThreadPool函數(shù)進行加鎖:

    static ThreadPool<T> *getThreadPool()
    {
        if (tp == nullptr)
        {
            pthread_mutex_lock(&lock);
            if (tp == nullptr)
            {
                tp = new ThreadPool<T>();
            }
            pthread_mutex_unlock(&lock);
        }
        return tp;
    }

對于上面的代碼:我們?yōu)槭裁匆讷@取鎖之前還要再加一個判斷指針為空的條件呢?

當已經(jīng)有一個線程創(chuàng)建出來了線程池的單例模式后,在這之后的所有其他線程即使申請到鎖,緊著著下一步就是去釋放鎖,它不會進入第二個 if 條件里面。其實這樣是效率低下的,因為線程會頻繁申請鎖,然后就釋放鎖。所以我們在最外層再加一個if判斷,就可以阻止后來的線程不用去申請鎖創(chuàng)建線程池了,直接返回已經(jīng)創(chuàng)建出來的線程池。

三、其他鎖

1、悲觀鎖:在每次取數(shù)據(jù)時,總是擔心數(shù)據(jù)會被其他線程修改,所以會在取數(shù)據(jù)前先加鎖(讀鎖,寫鎖,行鎖等),當其他線程想要訪問數(shù)據(jù)時,被阻塞掛起。

2、樂觀鎖:每次取數(shù)據(jù)時候,總是樂觀的認為數(shù)據(jù)不會被其他線程修改,因此不上鎖。但是在更新數(shù)據(jù)前,會判斷其他數(shù)據(jù)在更新前有沒有對數(shù)據(jù)進行修改。主要采用兩種方式:版本號機制和CAS操作。
~ CAS操作:當需要更新數(shù)據(jù)時,判斷當前內(nèi)存值和之前取得的值是否相等。如果相等則用新值更新。若不等則失敗,失敗則重試,一般是一個自旋的過程,即不斷重試。

3、自旋鎖:說到自旋鎖,我們不得不說一說我們之前所用到的鎖,我們之前所用的鎖都是互斥鎖,當線程沒有競爭到互斥鎖時,它會阻塞等待,只有等鎖被釋放了后,才能去重新申請鎖。而對于自旋鎖,當線程沒有競爭到自旋鎖的時候,線程會不斷地循環(huán)檢測去申請自旋鎖,直到拿到鎖。

一般來說,如果臨界區(qū)的代碼執(zhí)行時間比較長的話,我們是使用互斥鎖而不是自旋鎖的,這樣線程不會因為頻繁地檢測去申請鎖而占用CPU資源。如果臨界區(qū)的代碼執(zhí)行時間較短的話,我們一般就最好使用自旋鎖,而不是互斥鎖,因為互斥鎖申請失敗,是要阻塞等待,是需要發(fā)生上下文切換的,如果臨界區(qū)執(zhí)行的時間比較短,那可能上下文切換的時間會比臨界區(qū)代碼執(zhí)行的時間還要長。文章來源地址http://www.zghlxwxcb.cn/news/detail-855008.html

到了這里,關(guān)于Linux之 線程池 | 單例模式的線程安全問題 | 其他鎖的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權(quán),不承擔相關(guān)法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務器費用

相關(guān)文章

  • 【JAVA】Java 中什么叫單例設(shè)計模式?請用 Java 寫出線程安全的單例模式

    【JAVA】Java 中什么叫單例設(shè)計模式?請用 Java 寫出線程安全的單例模式

    ?? 個人博客: 個人主頁 ?? 個人專欄: JAVA ???? 功不唐捐,玉汝于成 目錄 前言 正文 懶漢式(Lazy Initialization): 雙重檢查鎖定(Double-Checked Locking): 結(jié)語 我的其他博客 在軟件設(shè)計中,單例設(shè)計模式是一種重要的設(shè)計思想,它確保了一個類只有一個實例,并提供了一

    2024年01月15日
    瀏覽(23)
  • Java 枚舉實現(xiàn)單例模式,線程安全又優(yōu)雅!

    Java 枚舉實現(xiàn)單例模式,線程安全又優(yōu)雅!

    這種DCL寫法的優(yōu)點:不僅線程安全,而且延遲加載。 1.1 為什么要double check?去掉第二次check行不行? 當然不行,當2個線程同時執(zhí)行g(shù)etInstance方法時,都會執(zhí)行第一個if判斷,由于鎖機制的存在,會有一個線程先進入同步語句,而另一個線程等待,當?shù)谝粋€線程執(zhí)行了 new Sin

    2024年02月02日
    瀏覽(20)
  • 【Java中23種設(shè)計模式-單例模式2--懶漢式2線程安全】

    【Java中23種設(shè)計模式-單例模式2--懶漢式2線程安全】

    加油,新時代打工人! 簡單粗暴,學習Java設(shè)計模式。 23種設(shè)計模式定義介紹 Java中23種設(shè)計模式-單例模式 Java中23種設(shè)計模式-單例模式2–懶漢式線程不安全 通過運行結(jié)果看,兩個線程的地址值是相同的,說明內(nèi)存空間里,創(chuàng)建了一個對象。

    2024年02月20日
    瀏覽(29)
  • linux線程池、基于線程池的單例模式、讀者寫者問題

    linux線程池、基于線程池的單例模式、讀者寫者問題

    線程池: 一種線程使用模式。線程過多會帶來調(diào)度開銷,進而影響緩存局部性和整體性能。而線程池維護著多個線程,等待著監(jiān)督管理者分配可并發(fā)執(zhí)行的任務。這避免了在處理短時間任務時創(chuàng)建與銷毀線程的代價。線程池不僅能夠保證內(nèi)核的充分利用,還能防止過分調(diào)度。

    2024年02月03日
    瀏覽(22)
  • 【多線程】線程安全的單例模式

    【多線程】線程安全的單例模式

    單例模式能保證某個類在程序中只存在 唯一 一份實例, 而不會創(chuàng)建出多個實例,從而節(jié)約了資源并實現(xiàn)數(shù)據(jù)共享。 比如 JDBC 中的 DataSource 實例就只需要一個. 單例模式具體的實現(xiàn)方式, 分成 “餓漢” 和 “懶漢” 兩種. 類加載的同時, 創(chuàng)建實例. 注意: 使用 static 修飾 instanc

    2024年02月09日
    瀏覽(18)
  • 線程安全之單例模式

    線程安全之單例模式

    這篇文章,我們會介紹一下單例模式,但這里的單例模式,不是我們所說的設(shè)計模式,當然聽到設(shè)計模式,大家一定都說,我當然知道設(shè)計模式了,有23種呢?一下子一頓輸出,當然我這里說的單例模式還是跟設(shè)計模式有一些區(qū)別的,當然我不做概述,因為我也沒咋個去了解過設(shè)計模式,我把

    2024年02月06日
    瀏覽(20)
  • 單例模式的線程安全形式

    目錄 1.單例設(shè)計模式的概念 2.實現(xiàn)方法: 1.餓漢式 2.懶漢式 3.區(qū)分餓漢式和懶漢式: 3.單例模式的雙重校驗線程安全形式 1.線程安全問題的解決方法 1.1 synchronized: 1.2 volatile: ????????保證變量可見性(不保證原子性) ? ? ? ? 禁止指令的重排序 2.線程安全

    2024年02月15日
    瀏覽(13)
  • 設(shè)計模式3:單例模式:靜態(tài)內(nèi)部類模式是怎么保證單例且線程安全的?

    設(shè)計模式3:單例模式:靜態(tài)內(nèi)部類模式是怎么保證單例且線程安全的?

    上篇文章:設(shè)計模式3:單例模式:靜態(tài)內(nèi)部類單例模式簡單測試了靜態(tài)內(nèi)部類單例模式,確實只生成了一個實例。我們繼續(xù)深入理解。 靜態(tài)變量什么時候被初始化? 這行代碼 private static Manager instance = new Manager(); 什么時候執(zhí)行? 編譯期間將.java文件轉(zhuǎn)為.class文件,運行期間

    2024年02月12日
    瀏覽(16)
  • 【Linux學習】多線程——線程池 | 單例模式

    【Linux學習】多線程——線程池 | 單例模式

    ??作者:一只大喵咪1201 ??專欄:《Linux學習》 ??格言: 你只管努力,剩下的交給時間! 多線程部分的知識講解到此就告一段落了,現(xiàn)在創(chuàng)建一個線程池來檢驗一下我們的學習成果。 一種線程使用模式,線程過多會帶來調(diào)度開銷,進而影響緩存局部性和整體性能。 線程池

    2024年02月09日
    瀏覽(24)
  • 【并發(fā)專題】單例模式的線程安全(進階理解篇)

    【并發(fā)專題】單例模式的線程安全(進階理解篇)

    最近學習了JVM之后,總感覺知識掌握不夠深,所以想通過分析經(jīng)典的【懶漢式單例】來加深一下理解。(主要是【靜態(tài)內(nèi)部類】實現(xiàn)單例的方式)。 如果小白想理解單例的話,也能看我這篇文章。我也通過了【前置知識】跟【普通懶漢式】、【雙檢鎖懶漢】、【靜態(tài)內(nèi)部類】

    2024年02月14日
    瀏覽(20)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包