第四章(同步併發操作)

1、條件變量

線程1完成後,通知其它線程它執行完成了。通過條件變量實現

void CTestThreadCondition::Preparation_Thread()
{
    while (true)
    {
        long ldata = clock();
        std::lock_guard<std::mutex> guard(dataMutex_);
        if (ldata >= lEndMark - 5000)
        {
            vsData_.push(ldata);
            data_cond_.notify_one();
            break;
        }
        std::this_thread::sleep_for(chrono::milliseconds(1000));
    }
}


void CTestThreadCondition::TestCondition()
{
    lEndMark = clock() + 15000;
    thread thread2(&CTestThreadCondition::Preparation_Thread, this);
    thread2.detach();

    while (true)
    {
        std::unique_lock<std::mutex> lk(dataMutex_);
        data_cond_.wait(lk, [this]{return !vsData_.empty(); }); // 匿名函數,等待vsData值是否不爲空了

        long lPop = vsData_.front();
        vsData_.pop();
        lk.unlock();

        cout << lPop<<endl;

        if (lPop >= lEndMark)
        {
            break;
        }
    }
}


此處需要使用unique_lock,因爲它裏面支持unlock,lock接口,而條件變量正是利用這兩個接口來實現通知等待的

wait接口需要傳入函數指針,用來判斷接受到信號後事繼續wait還是往下執行,部分wait源碼。收到notify_one後,wait(_Lck)就會往下執行

然後判斷!_Pred(),如果此時該函數還是返回false,那就需要等待下一次的notify_one了。

template<class _Predicate>
  void wait(unique_lock<mutex>& _Lck, _Predicate _Pred)
  { // wait for signal and test predicate
  while (!_Pred())
   wait(_Lck);
  }

wait(_Lck)中會先將lk執行unlock。wait成功接受會,再將其lock。此時如果有其它線程(比如notify_one後它的lk未執行unlock)佔用這把鎖,也是無法繼續往下執行的

條件變量時和信號量一起使用的,需要注意它對信號量的一些操作和改變,它依賴於信號量

notify_all是通知所有等待該信號的wait方法,notify_one是通知某一個,儘量一個信號一個等待。notify_all後信號量還是隻能一個鎖定


2、future

接受到線程的返回值

int Accumute(int i)
{
    Sleep(3000);
    return i;
}

void CTestThreadCondition::TestFutureGet()
{
    // async的構造函數接受的是一個右值引用&&
    // 第一個參數可以不傳,不傳與傳入async目前看來是一樣的,都表示在一個新的線程中立刻運行Accumute
    std::future<int> the_answer1 = std::async(std::launch::async, Accumute, 10);
    the_answer1.wait();  // wait會堵塞,直到the_answer1的線程執行返回結果
    int iRet1 = the_answer1.get(); // wait堵塞完後,get中存儲的該值。如果不適用wait直接用get也是一樣的會堵塞直到返回值

    std::future<int> the_answer2 = std::async(Accumute, 20); // 與上面一樣
    Sleep(4000);
    the_answer2.wait();
    int iRet2 = the_answer2.get();

    // 傳入deferred,表示不立刻運行,等到future調用wait或則get時纔開始運行
    // wait表示等待線程運行完成,get是獲取結果,也可以不調用wait,直接調用get也會進行等待線程執行完成的
    std::future<int> the_answer3 = std::async(std::launch::deferred, Accumute, 30);
    Sleep(4000);
    the_answer3.wait();
    int iRet3 = the_answer3.get();
}


3、packaged_task

將任務打包


void CTestThreadCondition::gui_thread()
{
    while (true)
    {
        packaged_task<int(int)> task; // 當前待處理的任務
        {
            lock_guard<mutex> lock(taskMutex_);
            if (Tasks_.empty())
            {
                Sleep(10);
                continue;
            }
            task = std::move(Tasks_.front());
            Tasks_.pop_front();
        }
        task(10); // 參數如何傳遞???
    }
}


void CTestThreadCondition::TestFuturePackaged()
{
    thread thread1(&CTestThreadCondition::gui_thread, this);
    // 將任務放到一個隊列中,然後線程不斷從隊列中取值進行執行
    packaged_task<int(int)> task(Accumute); // 將函數指針放到一個packaged_task中,支持通過task轉換成future,也可以直接執行task
    future<int> res = task.get_future();

    // task(1); // 也可以直接調用,然後res去get,此時會堵塞。task就是直接調用函數了
    {
        lock_guard<mutex> lock(taskMutex_);
        Tasks_.push_back(std::move(task)); // 放到隊列中,在其它線程取值
    }

    int iRet = res.get();
   
    thread1.join();
}


4、Promise

void CTestThreadCondition::TestFuturePromise()
{
    std::promise<int> PromiseTask;

    PromiseTask.set_value(Accumute(10)); // setvalue就開始執行Accumute了?

    future<int> futureTask = PromiseTask.get_future();

    int aa = futureTask.get();

    // 多次get會崩潰
    //int aa2 = futureTask.get();
}


5、shared_future

多個線程等待future的值

void CTestThreadCondition::TestSharedFuture()
{
    // future只能等待一個future。如果多個線程都想要得到future的值,多次get會崩潰。這是故意這樣設計的。爲了統一異步結果的所有權
    // 此時需要使用shared_future
    promise<int> p1;
    future<int> f(p1.get_future());
    assert(f.valid());

    shared_future<int> sf(std::move(f));
    // 也可以直接通過future的shared接口直接轉移所有權
    //shared_future<int> sf = f.share();

    assert(!f.valid());
    assert(sf.valid());
}


6、時鐘

chrono標準庫中部分接口,代替windows中sleep接口的方法:this_thread::sleep_for

sleep_for表示等待多少時間,sleep_unit表示等待到某個時間點。其它接口_for,_unit也是一樣的。

void CTestThreadCondition::TestTime()
{
    chrono::system_clock::time_point tmNow = chrono::system_clock::now();

    future<int> f = async(Accumute, 10);
    if (f.wait_for(std::chrono::microseconds(4*1000*1000)) == future_status::ready) // microseconds是微秒
    {
        cout<< "f.get()"<<f.get()<<endl;
    }

    future<int> f2 = async(Accumute, 10);
    if (f2.wait_until(chrono::steady_clock::now() + chrono::milliseconds(4000)) == future_status::ready) // milliseconds是毫秒
    {
        cout << "f2.get()" << f2.get() << endl;
    }
}


this_thread::sleep_for/sleep_until

conditon_variable::wait_for/wait_until

conditon_variable_any::wait_for/wait_until

time_mutex::try_lock_for/try_lock_until

recursive_time_mutex::try_lock_for/try_lock_unitl

unique_lock<timeLockable>::unique_lock/unique_lock(lockable,time_point/duration)

future<valuetype>::wait_for/wait_until

shared_future<valuetype>::wait_for/wait_until




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章