多線程筆記-創(chuàng)新互聯(lián)

多線程筆記
  • 設(shè)計(jì)模式
    • 單例設(shè)計(jì)模式(高頻使用)
    • 單例模式共享數(shù)據(jù)問題分析解決解決
    • std::call_once()
    • std::condition_variable ,wait(),notify_one,notify_all();
    • async future packaged_task promise
    • future其他成員函數(shù),shared_future atomic
    • 深入談?wù)刟sync
    • window臨界區(qū)
    • 自動(dòng)析構(gòu)技術(shù)
    • recursive_mutex遞歸的獨(dú)占互斥量
    • 帶超時(shí)的互斥量std::timed_mutex

成都創(chuàng)新互聯(lián)公司主要從事網(wǎng)站設(shè)計(jì)制作、成都網(wǎng)站建設(shè)、網(wǎng)頁(yè)設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)北辰,十多年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):028-86922220設(shè)計(jì)模式

大的項(xiàng)目分解成一個(gè)個(gè)小的模塊。

單例設(shè)計(jì)模式(高頻使用)

整個(gè)項(xiàng)目中,有某個(gè)或者某些特殊的類,屬于類的對(duì)象,只能創(chuàng)建一個(gè),多的創(chuàng)建不了。

// 單例模式示范代碼
class MyCas
{private:
    MyCas() {}  //私有化構(gòu)造函數(shù),避免該類創(chuàng)建多個(gè)
private:
    static MyCas* m_instance;
    static mutex m_mutex;
public:
    static MyCas* GetInstance()
    {if (m_instance == NULL)  //雙重鎖定,雙重檢查,保證只有當(dāng)m_instance不為空的時(shí)候才加鎖,提高效率
        {unique_lockmymutex(m_mutex); 
            if (m_instance == NULL)
            {m_instance = new MyCas();
                static CCarhuishouMyCas cl;  //生命周期直到程序結(jié)束,以至于當(dāng)這個(gè)對(duì)象釋放時(shí)調(diào)用析構(gòu)函數(shù)釋放new出來的對(duì)象
            }
        }
         return m_instance;
    }
    class CCarhuishouMyCas  //類中套類來釋放new出來的對(duì)象
    {public:
        ~CCarhuishouMyCas()
        {if (MyCas::m_instance)
            {delete MyCas::m_instance;
                MyCas::m_instance = NULL;
            }
        }
    };
};
MyCas* MyCas::m_instance = NULL;
mutex MyCas::m_mutex;
int main()
{MyCas* p_a = MyCas::GetInstance();
    return 0;
}
單例模式共享數(shù)據(jù)問題分析解決解決

若數(shù)據(jù)是只讀不寫的,就不需要加鎖保護(hù)
面臨的問題:需要多個(gè)線程中去創(chuàng)建單例類,這時(shí)有可能會(huì)出現(xiàn)GetInstance()這種成員函數(shù)要互斥
解決方法:就是要改進(jìn)getinstance函數(shù)

//改進(jìn)前
 static MyCas* GetInstance()
    {if (m_instance == NULL)
        {m_instance = new MyCas();
            static CCarhuishouMyCas cl;  //生命周期直到程序結(jié)束,以至于當(dāng)這個(gè)對(duì)象釋放時(shí)調(diào)用析構(gòu)函數(shù)釋放new出來的對(duì)象
        }
        return m_instance;
    }
   //改進(jìn)后
    static MyCas* GetInstance()
    {if (m_instance == NULL)  //雙重鎖定,雙重檢查,保證只有當(dāng)m_instance不為空的時(shí)候才加鎖,提高效率
        {unique_lockmymutex(m_mutex); 
            if (m_instance == NULL)
            {m_instance = new MyCas();
                static CCarhuishouMyCas cl;  //生命周期直到程序結(jié)束,以至于當(dāng)這個(gè)對(duì)象釋放時(shí)調(diào)用析構(gòu)函數(shù)釋放new出來的對(duì)象
            }
        }
        
        return m_instance;
    }
std::call_once()

c++11引入的函數(shù),該函數(shù)的第二個(gè)參數(shù)是一個(gè)函數(shù)名a();
call_once()功能可以保證函數(shù)a()只被調(diào)用一次
call_once()具備互斥量的能力,并且效率上比互斥量消耗的資源少。
它主要是通過綁定一個(gè)std::one_flag結(jié)構(gòu)(標(biāo)志位實(shí)現(xiàn)的)。
下面示范通過callz_once()改造單例模式,替代其互斥量。

// 單例模式示范代碼
class MyCas
{private:
    MyCas() {}  //私有化構(gòu)造函數(shù),避免該類創(chuàng)建多個(gè)
private:
    static MyCas* m_instance;
    static once_flag g_flag;
public:
    static void CreateInstance()//這里表示只被調(diào)用一次的函數(shù)
    {m_instance = new MyCas();
        static CCarhuishouMyCas cl;  //生命周期直到程序結(jié)束,以至于當(dāng)這個(gè)對(duì)象釋放時(shí)調(diào)用析構(gòu)函數(shù)釋放new出來的對(duì)象
    }
    static MyCas* GetInstance()
    {call_once(g_flag, CreateInstance);
        return m_instance;
    }
    class CCarhuishouMyCas  //類中套類來釋放new出來的對(duì)象
    {public:
        ~CCarhuishouMyCas()
        {if (MyCas::m_instance)
            {delete MyCas::m_instance;
                MyCas::m_instance = NULL;
            }
        }
    };
};
MyCas* MyCas::m_instance = NULL;
once_flag MyCas:: g_flag;
int main()
{MyCas* p_a = MyCas::GetInstance();
    return 0;
}
std::condition_variable ,wait(),notify_one,notify_all();

std::condition_variable
實(shí)際上是一個(gè)類,是需要和互斥量mutex配合使用,使用的時(shí)候要生成這個(gè)類
wait()
用來等待一個(gè)東西,當(dāng)?shù)诙€(gè)參數(shù)返回值是false時(shí)候,wait()將解鎖互斥量,并且堵塞到本行;堵塞到其他線程調(diào)用notify_one()成員函數(shù)為止如果沒有第二個(gè)參數(shù),那默認(rèn)效果和第二個(gè)參數(shù)為false一樣,立即堵塞。
當(dāng)其他線程調(diào)用notify_one()將wait喚醒后,wait就解除阻塞狀態(tài)。然后wait會(huì)不斷嘗試重新獲取互斥量的鎖,如果獲取不到wait會(huì)卡在這里等待鎖的獲取,當(dāng)獲取到鎖后,
如果wait第二個(gè)參數(shù)是lamda表達(dá)式,會(huì)繼續(xù)執(zhí)行l(wèi)amda表達(dá)式,此時(shí)如果為true,繼續(xù)執(zhí)行(此時(shí)還是加鎖狀態(tài)
如果此時(shí)沒有第二個(gè)參數(shù),則默認(rèn)是true;往下繼續(xù)執(zhí)行。一般來說wait()需要第二個(gè)參數(shù),防止虛假喚醒
notify_all() 則是喚醒全部wait(),在下面示例代碼中,其實(shí)notify_one 和notify_all效果是一樣的。

#include#include#include#include#includeusing namespace std;

class A {public:
    void inMsgRecvQueue()
    {for (int i = 0; i< 10000; i++)
        {cout<< "inmsgRecvqueue執(zhí)行,插入一個(gè)元素"<< i<guard1(my_mutex);
            msgRecvQueue.push_back(i);
            my_cond.notify_one();//嘗試把wait的線程喚醒,不一定能喚醒,如果線程此時(shí)沒有卡在wait哪里的話
        }
    }
    void outMsgRecvQueue()
    {while(1)
        {unique_lockguard1(my_mutex);
            my_cond.wait(guard1, [this] {if (!msgRecvQueue.empty())
                    return true;
                else
                    return false;
                });
                int k = msgRecvQueue.front();  
                cout<< "取出"<< k<< endl;
                msgRecvQueue.pop_front();   
                guard1.unlock(); //因?yàn)橛玫氖穷惸0宕藭r(shí)還是鎖著的,可以手動(dòng)解鎖,避免卡太長(zhǎng)時(shí)間
                //......一些操作
        }
    }
public:
    listmsgRecvQueue;
    mutex my_mutex;
    condition_variable my_cond;
};
int main()
{A myojia;
    std::thread myOutmsgobj(&A::outMsgRecvQueue, &myojia);
    std::thread myInmsgobj(&A::inMsgRecvQueue, &myojia);
    myOutmsgobj.join();
    myInmsgobj.join();
    return 0;
}
async future packaged_task promise

一 std::async
async是一個(gè)類模板,用于啟動(dòng)一個(gè)異步任務(wù)(即開啟一個(gè)線程來執(zhí)行對(duì)應(yīng)的線程入口函數(shù)),然后會(huì)返回一個(gè)futrue對(duì)象(里面包括了該線程返回的結(jié)果,可以通過futrue對(duì)象的成員函數(shù)get()來獲取結(jié)果) (future對(duì)象里面會(huì)保存一個(gè)值,在將來的某個(gè)時(shí)刻可以得到)

class A
{public:
    int mythread(int k )
    {cout<< "threadid"<< std::this_thread::get_id()<< endl;
        cout<< k<< endl;
        std::chrono::milliseconds dura(5000);
        std::this_thread::sleep_for(dura);
        return 5;
    }
};
int mythread()
{cout<< "threadid"<< std::this_thread::get_id()<< endl;
    std::chrono::milliseconds dura(5000);
    std::this_thread::sleep_for(dura);
    return 5;
}
int main()
{int tm = 12;
    class A a;
    cout<< "mainthreadid"<< std::this_thread::get_id()<< endl;
    futureresult = std::async(&A::mythread,&a,tm);
    cout<< ".........."<< endl;
    cout<< result.get()<< endl;   //如果線程沒執(zhí)行完,線程會(huì)卡在這里等待線程執(zhí)行完畢返回結(jié)果。
    cout<< "!!!!!!"<< endl;
    return 0;
}

此外,我們可以通過額外向async()傳遞一個(gè)參數(shù),該參數(shù)類型是launch類(枚舉類型)來達(dá)到一些效果
1,std::launch::deferred:表示線程如果函數(shù)調(diào)用延遲到std::future的wait()或者get()函數(shù)調(diào)用是才執(zhí)行,且如果不調(diào)用wait()和get(),該線程直接不執(zhí)行。即使調(diào)用wait()和get()也不創(chuàng)建新線程,都是主線程里面執(zhí)行線程入口函數(shù)。

futureresult = std::async(std::launch::deferred,&A::mythread,&a,tm);

2,std::launch::async:表示在調(diào)用async函數(shù)的時(shí)候就開始創(chuàng)建線程

futureresult = std::async(std::launch::async,&A::mythread,&a,tm);

二 std::packaged_task
packaged_task是一個(gè)類模板,模板參數(shù)是各種可調(diào)用對(duì)象,通過packaged_task包裝起來,方便將來作為線程入口函數(shù)來調(diào)用

int mythread(int k)
{cout<< "threadid"<< std::this_thread::get_id()<< endl;
    cout<< k<< endl;
    std::chrono::milliseconds dura(5000);
    std::this_thread::sleep_for(dura);
    return 5;
}
int main()
{cout<< "mainthreadid"<< std::this_thread::get_id()<< endl;
    packaged_taskmypt(mythread);
    thread t1(ref(mypt), 1);  //ref()指用引用的方式傳遞
    t1.join();
    std::futureresult = mypt.get_future();
    cout<< result.get()<< endl;
    cout<< "!!!!!"<< endl;
    return 0;
}

//改為lamda表達(dá)式
int main()
{cout<< "mainthreadid"<< std::this_thread::get_id()<< endl;
    packaged_taskmypt([](int k) {
        cout<< "threadid"<< std::this_thread::get_id()<< endl;
        cout<< k<< endl;
        std::chrono::milliseconds dura(5000);
        std::this_thread::sleep_for(dura);
        return 5;        
        });
    thread t1(ref(mypt), 1);  //ref()指用引用的方式傳遞
    t1.join();
    std::futureresult = mypt.get_future();
    cout<< result.get()<< endl;
    cout<< "!!!!!"<< endl;
    return 0;
}
//也可以直接調(diào)用,但是就都在主線程中執(zhí)行了。
int main()
{cout<< "mainthreadid"<< std::this_thread::get_id()<< endl;
    packaged_taskmypt([](int k) {
        cout<< "threadid"<< std::this_thread::get_id()<< endl;
        cout<< k<< endl;
        std::chrono::milliseconds dura(5000);
        std::this_thread::sleep_for(dura);
        return 5;        
        });
    mypt(100);
    std::futureresult = mypt.get_future();
   cout<< result.get()<< endl;
    cout<< "!!!!!"<< endl;
    return 0;
}

//和容器關(guān)聯(lián)packaged_task
vector>mytasks;
int main()
{cout<< "mainthreadid"<< std::this_thread::get_id()<< endl;
    packaged_taskmypt([](int k) {
        cout<< "threadid"<< std::this_thread::get_id()<< endl;
        cout<< k<< endl;
        std::chrono::milliseconds dura(5000);
        std::this_thread::sleep_for(dura);
        return 5;        
        });
    mytasks.push_back(std::move(mypt));//通過std::move,可以避免不必要的拷貝操作,是為性能而生,將對(duì)象的狀態(tài)或者所有權(quán)從一個(gè)對(duì)象轉(zhuǎn)移到另一個(gè)對(duì)象,只是轉(zhuǎn)移,沒有內(nèi)存的搬遷或者內(nèi)存拷貝。 
    packaged_taskmypt2;
    auto it = mytasks.begin();
    mypt2 = std::move(*it);
    mytasks.erase(it);  //刪除第一個(gè)元素,迭代器已經(jīng)失效了,不能再使用it;
    mypt2(123);
    futureresult = mypt2.get_future();
    cout<< result.get()<< endl;
    return 0;
}

二 std::promise
promise
可以能夠在某個(gè)線程中給它賦值,然后我們可以在其他線程中,把這個(gè)值給取出來

void mythread(promise& tmmp, int calc)
{calc++;
    calc *= 10;
    std::chrono::milliseconds dur(5000);
    std::this_thread::sleep_for(dur);
    int result = calc;
    tmmp.set_value(result);
    return;
}
void mythread2(futuretmmp)
{auto k = tmmp.get();
    cout<< "thread2 "<< k<< endl;
    return;
}
int main()
{cout<< "mainthreadid"<< std::this_thread::get_id()<< endl;
    promisemyprom;
    thread t1(mythread, ref(myprom), 12);
    t1.join();
    thread t2(mythread2,myprom.get_future());
    t2.join();
    return 0;
}
future其他成員函數(shù),shared_future atomic

future_status 有三種狀態(tài):timeout ready deferred

class A
{public:
    int mythread(int k)
    {cout<< "threadid"<< std::this_thread::get_id()<< endl;
        cout<< k<< endl;
        std::chrono::milliseconds dura(5000);
        std::this_thread::sleep_for(dura);
        return 5;
    }
};
int main()
{int tm = 12;
    class A a;
    cout<< "mainthreadid"<< std::this_thread::get_id()<< endl;
    futureresult = std::async( &A::mythread, &a, tm);
    //futureresult = std::async(std::launch::deferred, &A::mythread, &a,tm);
    cout<< ".........."<< endl;
   // cout<< result.get()<< endl;   //如果線程沒執(zhí)行完,線程會(huì)卡在這里等待線程執(zhí)行完畢返回結(jié)果。
    future_status status = result.wait_for(std::chrono::seconds(6));
    if (status == future_status::timeout)  //超時(shí),指的的等待時(shí)間結(jié)束后,線程還沒有運(yùn)行完
    {cout<< "超時(shí),線程還沒有執(zhí)行完"<< endl;
    }
    else if (status == future_status::ready) // 準(zhǔn)備好,表示等待時(shí)間結(jié)束后,線程已經(jīng)運(yùn)行完了
    {cout<< "線程已經(jīng)執(zhí)行完畢"<< endl;
    }
    else if (status == future_status::deferred) //如果async第一個(gè)參數(shù)是deferred,該條件成立
    {cout<< "線程被延遲執(zhí)行"<< endl;
        cout<< result.get()<< endl;
    }
    cout<< "!!!!!!"<< endl;
    return 0;
}

std::shared_future (主要是為了解決future_get()只能被調(diào)用一次的問題(這里的get其實(shí)這里是轉(zhuǎn)移的意思),適合用于多個(gè)線程使用同一個(gè)future,本質(zhì)就是把get()的語(yǔ)義由轉(zhuǎn)移變?yōu)榭截?

int mythread(int k)
{cout<< "threadid"<< std::this_thread::get_id()<< endl;
    std::chrono::milliseconds dura(5000);
    std::this_thread::sleep_for(dura);
    return 5;
}
int mythread2(shared_future& g)
{cout<< "get_threadid"<< std::this_thread::get_id()<< endl;
    cout<< g.get()<< endl;
    return 5;
}
int main()
{cout<< "mainthreadid"<< std::this_thread::get_id()<< endl;
    packaged_taskmypt(mythread);
    thread t1(ref(mypt), 1);  //ref()指用引用的方式傳遞
    t1.join();
    std::futureresult = mypt.get_future();         
    std::shared_futureresult2(move(result));//或者 std::shared_futureresult2(result.share());
    //或者直接std::shared_futureresult2(mypt.get_future());
    if (result.valid())  //可用于判斷result是否可以被get,就是是否為空 
    {cout<< "result有效"<< endl;
    }
    thread t2(mythread2, ref(result2));  //ref()指用引用的方式傳遞    
    thread t3(mythread2, ref(result2));  //ref()指用引用的方式傳遞
    thread t4(mythread2, ref(result2));  //ref()指用引用的方式傳遞
    t2.join();
    t3.join();
    t4.join();
    cout<< "!!!!!"<< endl;
    return 0;
}

原子操作:: std::atomic(比加鎖效率高,頻繁加鎖影響效率)
原子操作可以理解為一種不需要互斥量加鎖(無(wú)鎖)技術(shù)的多線程并發(fā)編程方式
原子操作:在多線程中不會(huì)被打斷的程序執(zhí)行片段
原子操作和互斥量的使用場(chǎng)景不同(原子:即不可分割)
互斥量加鎖一般針對(duì)一個(gè)代碼段,而原子操作針對(duì)的一般都是一個(gè)變量,而不是一個(gè)代碼段
一般用來處理變量的簡(jiǎn)單操作
注意:一般的atomic原子操作,針對(duì)++,–,+=,&=,|=,^=,-=是支持的。其他的可能不支持,遇到這種情況,可以寫一小段代碼進(jìn)行測(cè)試
原子操作不支持拷貝構(gòu)造函數(shù)
例如:

以下這些都是不被允許的
atomic<>atm=0;
atomicatm2=atm
autoatm3=atm
若實(shí)在想復(fù)制,可以調(diào)用load()函數(shù)
load():以原子的方式讀atomic的值
atomicatm2(atm.load())
autoatm3(atm.load())
store():以原子方式寫入內(nèi)容
atm2.store(12)  //atm2=12

下面將用互斥量和鎖進(jìn)行一個(gè)對(duì)比,并結(jié)合c++時(shí)間戳來比較兩者之間的效率差距

//使用原子操作需要時(shí)間
#include#include#include#include#includeusing namespace std;
atomicm_count = 0;
void mythread()
{for (int i = 0; i< 20000000; i++)
    {m_count++;
    }
}
int main()
{auto tpbegin = chrono::duration_cast(chrono::high_resolution_clock::now().time_since_epoch());
    thread t1(mythread);
    thread t2(mythread);
    t1.join();
    t2.join();
    auto tpend = chrono::duration_cast(chrono::high_resolution_clock::now().time_since_epoch());
    cout<<"累計(jì) 40000000所需時(shí)間:"<< tpend.count()- tpbegin.count()<<"ms"<< endl;
    cout<< m_count<< endl;
    return 0;
}
累計(jì) 40000000所需時(shí)間:1098ms
40000000

//使用互斥量需要時(shí)間
#include#include#include#include#includeusing namespace std;
int m_count = 0;
mutex m_gmutex;
void mythread()
{for (int i = 0; i< 20000000; i++)
    {m_gmutex.lock();
        m_count++;
        m_gmutex.unlock();
    }
}
int main()
{auto tpbegin = chrono::duration_cast(chrono::high_resolution_clock::now().time_since_epoch());
    thread t1(mythread);
    thread t2(mythread);
    t1.join();
    t2.join();
    auto tpend = chrono::duration_cast(chrono::high_resolution_clock::now().time_since_epoch());
    cout<<"累計(jì) 40000000所需時(shí)間:"<< tpend.count()- tpbegin.count()<<"ms"<< endl;
    cout<< m_count<< endl;
    return 0;
}

累計(jì) 40000000所需時(shí)間:6408ms
40000000

綜合上述,在累計(jì)40000000操作后,原子操作比互斥操作快6倍左右,隨著次數(shù)越來越多,差距會(huì)越大。

深入談?wù)刟sync

async有兩個(gè)參數(shù)
std::launch::deferred【延遲調(diào)用】
std::launch::async【強(qiáng)制創(chuàng)建一個(gè)線程】
用thread就說明創(chuàng)建線程,對(duì)于async來說,一般不叫它創(chuàng)建線程(雖然效果是能夠創(chuàng)建線程),一般叫它為創(chuàng)建一個(gè)異步任務(wù)
async和thread最明顯的不同是,async有時(shí)候并不創(chuàng)建線程(用std::launch::deferred參數(shù)的時(shí)候)
(a)當(dāng)使用std::launch::deferred參數(shù)的時(shí)候,并不會(huì)創(chuàng)建新的線程,他會(huì)延遲到future對(duì)象調(diào)用.get()或.wait()的時(shí)候才執(zhí)行入口函數(shù),如果沒 有調(diào)用.get()或.wait(),甚至連入口函數(shù)都不執(zhí)行
(b)當(dāng)使用std::launch::async時(shí)候,會(huì)強(qiáng)制這個(gè)異步任務(wù)在新線程上面執(zhí)行,意味著,系統(tǒng)必須要?jiǎng)?chuàng)建出一個(gè)新線程來運(yùn)行入口函數(shù)
?當(dāng)使用std::launch::deferred|std::launch::async,系統(tǒng)會(huì)根據(jù)情況自己選擇一種執(zhí)行
(d)若不使用參數(shù),效果和std::launch::deferred|std::launch::async一樣的,系統(tǒng)會(huì)自行決定會(huì)同步調(diào)用還是異步調(diào)用

所謂的自行決定是異步(創(chuàng)建新線程)還是同步(不創(chuàng)建新線程)方式運(yùn)行
(1)async和thread區(qū)別
(a)使用thread方式創(chuàng)建線程,不容易拿到線程的返回值(要拿可能需要使用全局變量的方式)
使用async創(chuàng)建異步任務(wù),可以很輕松的通過future獲得線程的返回值
(b)用thread創(chuàng)建線程,如果系統(tǒng)資源緊張,有可能會(huì)創(chuàng)建線程失敗,容易導(dǎo)致程序崩潰。
使用async一般不會(huì)崩潰,因?yàn)槿绻到y(tǒng)資源緊張的時(shí)候,async這種不加額外參數(shù)的調(diào)用就不會(huì)創(chuàng)建新線程,而是后續(xù)調(diào)用result.get()來請(qǐng)求結(jié)果,判斷是同步還是異步執(zhí)行。
經(jīng)驗(yàn):一個(gè)程序里面,線程數(shù)量不宜超過100-200
(2)當(dāng)使用async,不用參數(shù)的時(shí)候那如果判斷是同步還是異步,需要使用future_status來判斷

future_status status = result.wait_for(std::chrono::seconds(0));
    if (status == future_status::timeout)  //超時(shí),指的的等待時(shí)間結(jié)束后,線程還沒有運(yùn)行完
    {cout<< "超時(shí),線程還沒有執(zhí)行完"<< endl;
    }
    else if (status == future_status::ready) // 準(zhǔn)備好,表示等待時(shí)間結(jié)束后,線程已經(jīng)運(yùn)行完了
    {cout<< "線程已經(jīng)執(zhí)行完畢"<< endl;
    }
    //上面兩種情況說明都是異步執(zhí)行,從async創(chuàng)建時(shí)候線程就執(zhí)行了
    else if (status == future_status::deferred) //如果async第一個(gè)參數(shù)是deferred,該條件成立
    {cout<< "線程被延遲執(zhí)行"<< endl;
        cout<< result.get()<< endl;   //如果是這種情況,只有當(dāng).get()是函數(shù)才開始跑,且在同一個(gè)線程里面執(zhí)行。
    }
window臨界區(qū)
#include#include#include#include#includeusing namespace std;
#define _WINDOWSJQ_   //開關(guān)

class A {public:
    void inMsgRecvQueue()
    {for (int i = 0; i< 100; i++)
        {cout<< "inmsgRecvqueue執(zhí)行,插入一個(gè)元素"<< i<< endl;
#ifdef _WINDOWSJQ_
            EnterCriticalSection(&my_winsec);
             msgRecvQueue.push_back(i);
            LeaveCriticalSection(&my_winsec);
#else
            unique_lockguard1(my_mutex);
            msgRecvQueue.push_back(i);
#endif // _WINDOWSJQ_
        }
    }
    void outMsgRecvQueue()
    {while (1)
        {if (!msgRecvQueue.empty())
            {if (!msgRecvQueue.empty())
                {#ifdef _WINDOWSJQ_
                    EnterCriticalSection(&my_winsec);
                    int k = msgRecvQueue.front();
                    cout<< "取出"<< k<< endl;
                    msgRecvQueue.pop_front();
                    LeaveCriticalSection(&my_winsec);
#else
                    unique_lockguard1(my_mutex);
                    int k = msgRecvQueue.front();
                    cout<< "取出"<< k<< endl;
                    msgRecvQueue.pop_front();
#endif // _WINDOWSJQ_   
                }
            } 
           
        }
    }
    A()
    {#ifdef _WINDOWSJQ_
        InitializeCriticalSection(& my_winsec);//windows中的臨界區(qū)使用必須初始化 
#endif // _WINDOWSJQ_

    }
public:
    listmsgRecvQueue;
    mutex my_mutex;
    condition_variable my_cond;

#ifdef _WINDOWSJQ_
    CRITICAL_SECTION my_winsec;//windows中的臨界區(qū),非常類似于互斥量mutex
#endif // _WINDOWSJQ_

};
int main()
{A myojia;
    std::thread myOutmsgobj(&A::outMsgRecvQueue, &myojia);
    std::thread myInmsgobj(&A::inMsgRecvQueue, &myojia);
    myOutmsgobj.join();
    myInmsgobj.join();
    return 0;
}

在同一線程中,windows中可以多次進(jìn)入臨界區(qū)。但是調(diào)用幾次臨界區(qū),就需要調(diào)用出幾次臨界區(qū)。而在c++11中,不允許同一個(gè)線程lock一個(gè)互斥量多次,否則報(bào)異常。

自動(dòng)析構(gòu)技術(shù)

下面把win臨界區(qū)封裝成一個(gè)類,本類自動(dòng)釋放臨界區(qū),類似mutex的lock_guard
這種叫做RAII類,即(Resource Acquisition is initialization) “資源獲取即初始化”,容器,智能指針都是raii類

#include#include#include#include#includeusing namespace std;
#define _WINDOWSJQ_   //開關(guān)

//本類自動(dòng)釋放臨界區(qū),類似mutex的lock_guard
//這種叫做RAII類,即(Resource Acquisition is initialization) “資源獲取即初始化”,容器,智能指針都是raii類
class winlock
{public:
    winlock(CRITICAL_SECTION* winsec)
    {my_winsec = winsec;
        EnterCriticalSection(my_winsec);
    }
    ~winlock()
    {LeaveCriticalSection(my_winsec);
    }
    CRITICAL_SECTION *my_winsec;
};
class A {public:
    void inMsgRecvQueue()
    {for (int i = 0; i< 100; i++)
        {cout<< "inmsgRecvqueue執(zhí)行,插入一個(gè)元素"<< i<< endl;
#ifdef _WINDOWSJQ_
            winlock win(&my_winsec);
            msgRecvQueue.push_back(i);
            
#else
            unique_lockguard1(my_mutex);
            msgRecvQueue.push_back(i);
#endif // _WINDOWSJQ_
        }
    }
    void outMsgRecvQueue()
    {while (1)
        {if (!msgRecvQueue.empty())
            {if (!msgRecvQueue.empty())
                {#ifdef _WINDOWSJQ_
                    winlock win(&my_winsec);
                    int k = msgRecvQueue.front();
                    cout<< "取出"<< k<< endl;
                    msgRecvQueue.pop_front();
#else
                    unique_lockguard1(my_mutex);
                    int k = msgRecvQueue.front();
                    cout<< "取出"<< k<< endl;
                    msgRecvQueue.pop_front();
#endif // _WINDOWSJQ_   
                }
            }

        }
    }
    A()
    {#ifdef _WINDOWSJQ_
        InitializeCriticalSection(&my_winsec);//windows中的臨界區(qū)使用必須初始化 
#endif // _WINDOWSJQ_

    }
public:
    listmsgRecvQueue;
    mutex my_mutex;
    condition_variable my_cond;

#ifdef _WINDOWSJQ_
    CRITICAL_SECTION my_winsec;//windows中的臨界區(qū),非常類似于互斥量mutex
#endif // _WINDOWSJQ_

};
int main()
{A myojia;
    std::thread myOutmsgobj(&A::outMsgRecvQueue, &myojia);
    std::thread myInmsgobj(&A::inMsgRecvQueue, &myojia);
    myOutmsgobj.join();
    myInmsgobj.join();
    return 0;
}
recursive_mutex遞歸的獨(dú)占互斥量

主要是為了解決重復(fù)lock會(huì)報(bào)錯(cuò)的問題
std::mutex:獨(dú)占互斥量,自己lock了,別人lock不了
recursive_mutex:遞歸的獨(dú)占互斥量:允許同一個(gè)線程,同一個(gè)互斥量多次lock。

#include#include#include#include#includeusing namespace std;
//#define _WINDOWSJQ_   //開關(guān)

//本類自動(dòng)釋放臨界區(qū),類似mutex的lock_guard
//這種叫做RAII類,即(Resource Acquisition is initialization) “資源獲取即初始化”,容器,智能指針都是raii類
class winlock
{public:
    winlock(CRITICAL_SECTION* winsec)
    {my_winsec = winsec;
        EnterCriticalSection(my_winsec);
    }
    ~winlock()
    {LeaveCriticalSection(my_winsec);
    }
    CRITICAL_SECTION *my_winsec;
};
class A {public:
    void inMsgRecvQueue()
    {for (int i = 0; i< 100; i++)
        {cout<< "inmsgRecvqueue執(zhí)行,插入一個(gè)元素"<< i<< endl;
#ifdef _WINDOWSJQ_
            winlock win(&my_winsec);
            msgRecvQueue.push_back(i);
            
#else
            unique_lockguard1(my_mutext);//這里相當(dāng)與lock了三次
            testfun2();
            msgRecvQueue.push_back(i);
#endif // _WINDOWSJQ_
        }
    }
    void outMsgRecvQueue()
    {while (1)
        {if (!msgRecvQueue.empty())
            {if (!msgRecvQueue.empty())
                {#ifdef _WINDOWSJQ_
                    winlock win(&my_winsec);
                    int k = msgRecvQueue.front();
                    cout<< "取出"<< k<< endl;
                    msgRecvQueue.pop_front();
#else
                    unique_lockguard1(my_mutext);
                    int k = msgRecvQueue.front();
                    cout<< "取出"<< k<< endl;
                    msgRecvQueue.pop_front();
#endif // _WINDOWSJQ_   
                }
            }

        }
    }
    A()
    {#ifdef _WINDOWSJQ_
        InitializeCriticalSection(&my_winsec);//windows中的臨界區(qū)使用必須初始化 
#endif // _WINDOWSJQ_

    }
public:
    listmsgRecvQueue;
    condition_variable my_cond;
    std::recursive_mutex my_mutext;

#ifdef _WINDOWSJQ_
    CRITICAL_SECTION my_winsec;//windows中的臨界區(qū),非常類似于互斥量mutex
#endif // _WINDOWSJQ_

    void testfun1()
    {lock_guardsbguard(my_mutext);
        //干一些事情
    }
    void testfun2()
    {lock_guardsbguard(my_mutext);
        //干一些事情
        testfun1();
    }
};
int main()
{A myojia;
    std::thread myOutmsgobj(&A::outMsgRecvQueue, &myojia);
    std::thread myInmsgobj(&A::inMsgRecvQueue, &myojia);
    myOutmsgobj.join();
    myInmsgobj.join();
    return 0;
}

但是這種多次鎖效率肯定比mutex效率差,如果通過改代碼能改成只lock一次最好。

帶超時(shí)的互斥量std::timed_mutex

std::timed_mutex帶超時(shí)的獨(dú)占互斥量
有兩個(gè)參數(shù):
try_lock_for();等待一段時(shí)間,無(wú)論有沒有拿到鎖,程序都走下去
try_lock_until();參數(shù)是一個(gè)未來的時(shí)間點(diǎn),在這個(gè)未來的時(shí)間沒到的時(shí)間內(nèi),如果拿到鎖,就走下去,到時(shí)間沒拿到,也走下去。
try_lock_for()示例

#include#include#include#include#includeusing namespace std;
//#define _WINDOWSJQ_   //開關(guān)

//本類自動(dòng)釋放臨界區(qū),類似mutex的lock_guard
//這種叫做RAII類,即(Resource Acquisition is initialization) “資源獲取即初始化”,容器,智能指針都是raii類
class winlock
{public:
    winlock(CRITICAL_SECTION* winsec)
    {my_winsec = winsec;
        EnterCriticalSection(my_winsec);
    }
    ~winlock()
    {LeaveCriticalSection(my_winsec);
    }
    CRITICAL_SECTION *my_winsec;
};
class A {public:
    void inMsgRecvQueue()
    {for (int i = 0; i< 100; i++)
        {
#ifdef _WINDOWSJQ_
            winlock win(&my_winsec);
            msgRecvQueue.push_back(i);
#else
            chrono::milliseconds timeout(100);;
            if (my_mutext.try_lock_for(timeout))//等待100ms來獲取鎖
            {cout<< "inmsgRecvqueue執(zhí)行,插入一個(gè)元素"<< i<< endl;
                msgRecvQueue.push_back(i);
                my_mutext.unlock(); //拿到鎖用完了記得釋放
            }
            else
            {//沒有拿到鎖休眠100ms
                chrono::milliseconds dur(100);
                std::this_thread::sleep_for(dur);
            }
#endif // _WINDOWSJQ_
        }
    }
    void outMsgRecvQueue()
    {while (1)
        {if (!msgRecvQueue.empty())
            {if (!msgRecvQueue.empty())
                {#ifdef _WINDOWSJQ_
                    winlock win(&my_winsec);
                    int k = msgRecvQueue.front();
                    cout<< "取出"<< k<< endl;
                    msgRecvQueue.pop_front();
#else
                    unique_lockguard1(my_mutext);
                    chrono::milliseconds dur(1000);
                    std::this_thread::sleep_for(dur);
                    int k = msgRecvQueue.front();
                    cout<< k<< endl;
                    msgRecvQueue.pop_front();
                    
#endif // _WINDOWSJQ_   
                }
            }

        }
    }
    A()
    {#ifdef _WINDOWSJQ_
        InitializeCriticalSection(&my_winsec);//windows中的臨界區(qū)使用必須初始化 
#endif // _WINDOWSJQ_

    }
public:
    listmsgRecvQueue;
    timed_mutex my_mutext;
#ifdef _WINDOWSJQ_
    CRITICAL_SECTION my_winsec;//windows中的臨界區(qū),非常類似于互斥量mutex
#endif // _WINDOWSJQ_

};
int main()
{A myojia;
    std::thread myOutmsgobj(&A::outMsgRecvQueue, &myojia);
    std::thread myInmsgobj(&A::inMsgRecvQueue, &myojia);
    myOutmsgobj.join();
    myInmsgobj.join();
    return 0;
}

try_lock_until()示例
上面替換即可。

chrono::milliseconds timeout(100);
            if (my_mutext.try_lock_until(chrono::steady_clock::now()+timeout))//等待100ms來獲取鎖
            {cout<< "inmsgRecvqueue執(zhí)行,插入一個(gè)元素"<< i<< endl;
                msgRecvQueue.push_back(i);
                my_mutext.unlock(); //拿到鎖用完了記得釋放
            }
            else
            {//沒有拿到鎖休眠100ms
                chrono::milliseconds dur(100);
                std::this_thread::sleep_for(dur);
            }

recursive_mutex也有一個(gè)類似的std::recursive_timed_mutex
可以多次加鎖。

你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級(jí)服務(wù)器適合批量采購(gòu),新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧

本文名稱:多線程筆記-創(chuàng)新互聯(lián)
URL地址:http://www.muchs.cn/article46/pcceg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站設(shè)計(jì)公司域名注冊(cè)、靜態(tài)網(wǎng)站、小程序開發(fā)、全網(wǎng)營(yíng)銷推廣網(wǎng)站導(dǎo)航

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

外貿(mào)網(wǎng)站制作