簡體   English   中英

"C++:來自 std::thread 的簡單返回值?"

[英]C++: Simple return value from std::thread?

使用 win32 線程,我有直接的GetExitCodeThread() ,它給了我線程函數返回的值。 我正在為std::thread (或提升線程)尋找類似的東西
據我了解,這可以通過期貨來完成,但究竟如何?

請參閱有關 C++11 期貨的視頻教程

明確地使用線程和期貨:

#include <thread>
#include <future>

void func(std::promise<int> && p) {
    p.set_value(1);
}

std::promise<int> p;
auto f = p.get_future();
std::thread t(&func, std::move(p));
t.join();
int i = f.get();

或者使用std::async (線程和期貨的高級包裝器):

#include <thread>
#include <future>
int func() { return 1; }
std::future<int> ret = std::async(&func);
int i = ret.get();

我無法評論它是否適用於所有平台(它似乎適用於 Linux,但不適用於帶有 GCC 4.6.1 的 Mac OSX)。

我會說:

#include <thread>
#include <future>

int simplefunc(std::string a)
{ 
    return a.size();
}

int main()
{
      auto future = std::async(simplefunc, "hello world");
      int simple = future.get();

      return simple;
}

請注意,異步甚至會傳播從線程函數拋出的任何異常

我認為參考變量讀起來更直觀。

std::mutex m;

void threadFunction(int& val) {
    std::lock_guard<std::mutex> guard(m);
    val = 5;
}

int x = 0;
std::thread theThread(threadFunction, std::ref(x));
theThread.join();
std::cout << x << std::endl;
// prints 5

使用 C++11 線程,無法在線程退出時獲得返回值,而pthread_exit(...)

您需要使用 C++11 Future<>來獲取返回值。 Future 是使用模板化參數創建的,其中模板采用返回值(內置於用戶定義類型)。

您可以使用future<..>::get(..)函數在另一個線程中獲取值。

使用future<..>一個好處是你可以檢查返回值的有效性,即如果它已經被采用,你get()通過使用future<..>::isValid(...)檢查有效性來避免意外調用get() future<..>::isValid(...)功能。

以下是您將如何編寫代碼。

#include <iostream>
#include <future>
using namespace std;
auto retFn() {
    return 100;
}
int main() {
    future<int> fp = async(launch::async, retFn);
    if(fp.valid())
       cout<<"Return value from async thread is => "<<fp.get()<<endl;
    return 0;
}

還應該注意的是,我們可以通過使用launch::deferred選項讓未來在同一線程上運行

 future<int> fp = async(launch::deferred, retFn);

使用std::ref將引用/指針傳遞給線程

async只是比這更好,但只是為了科學,它可以做到:

void myfunc_reference(int& i);
std::thread(myfunc_reference, std::ref(output));

我懷疑async的實現必須在幕后為我們做一些事情,這本質上是你必須在pthread后端做的事情: 如何從 C 中的 pthread 線程返回值?

當然,您必須確保變量生命周期持續到線程返回。

下面的可運行代碼示例比較了 async 和這種更糟糕的方法:

主程序

#include <cassert>
#include <future>
#include <iostream>
#include <thread>
#include <vector>

int myfunc(int i) {
    return i + 1;
}

void myfunc_reference(int& i) {
    i = myfunc(i);
}

int main() {
    unsigned int nthreads = 4;
    std::vector<int> inputs{1, 2, 3, 4};
    std::vector<int> outputs_expect{2, 3, 4, 5};

    // future and sync. Nirvana. When you are not fighting to death with types:
    // https://stackoverflow.com/questions/10620300/can-stdasync-be-use-with-template-functions
    {
        std::vector<std::future<int>> futures(nthreads);
        std::vector<int> outputs(nthreads);
        for (decltype(futures)::size_type i = 0; i < nthreads; ++i) {
            futures[i] = std::async(
                myfunc,
                inputs[i]
            );
        }
        for (decltype(futures)::size_type i = 0; i < nthreads; ++i) {
            outputs[i] = futures[i].get();
        }
        assert(outputs_expect == outputs);
    }

    // Reference arguments.
    //
    // Annoying because requires:
    //
    // - wrapping the return function to accept references
    // - keeping an array of outputs
    // - std::ref
    {
        std::vector<std::thread> threads(nthreads);
        std::vector<int> inouts(inputs);
        for (decltype(threads)::size_type i = 0; i < nthreads; ++i) {
            threads[i] = std::thread(myfunc_reference, std::ref(inouts[i]));
        }
        for (auto& thread : threads) {
            thread.join();
        }
        assert(outputs_expect == inouts);
    }
}

GitHub 上游.

編譯並運行:

g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp -pthread
./main.out

在 Ubuntu 19.04 中測試。

這是一個更具體的例子。

函數使用回調參數模擬下載以顯示進度並取消下載。

namespace __HeavyWork
{
    int SimulateDownload(std::function<int(int)> dlCallBack)
    {
        for (size_t i = 0; i < 100; i++)
        {
            std::this_thread::sleep_for(std::chrono::seconds(1));

            if (dlCallBack(i) == -1)
            {
                return i;
            }
        }

        return 100;
    }
}

我們想獲取下載狀態,

#include <thread>
#include <future>

    void test()
    {
        auto simulateCancellation = []()->bool {
            static bool b = true;
            if (b)
            {
                srand((unsigned int)time(NULL));
                b = false;
            }
            return (rand() % 7) == 0;
        };

        auto funDLCallback = [&](int i)->int {
            if (simulateCancellation())
            {
                return -1;
            }
            cout << "download: " << i << endl;
            return i;
        };
        auto funDownload = [&](std::promise<int> && p) {
            p.set_value(__HeavyWork::SimulateDownload(funDLCallback));
        };



        std::promise<int> p;
        auto f = p.get_future();
        std::thread t(funDownload, std::move(p));
        //dlg.doModal();
        t.join();
        cout << "return value: " << f.get() << endl;
    }

您可以使用以下帶有線程和期貨的代碼創建一組線程:

#include <thread>
#include <future>

void func(promise<float> && prms) {

    prms.set_value(0.125);

}

這一切都取決於您對“簡單”的定義。

使用期貨肯定會在幾行 C++ 中實現魔術,但我發現劫持一個專為並行處理而設計的機制來實現這種微不足道的用途是值得商榷的。

futures 主要在多核 CPU 上有意義,它們允許進程啟動不同步的任務,這些任務將從其他內核中汲取計算能力(撇開這樣一個事實,即找到一組大到值得付出努力的不相關數據並不是一件容易的事也沒關系)。

使用整個機制作為一種變通方法來檢索純粹的 int 返回值,這就是我所說的語法驅動的軟件設計。 它很好地展示了 C++11 的多功能性,但它確實在語法糖霜下隱藏了大量的資源消耗。

如果不是使用其他內核作為原始電源,如果您不想在線程完成之前與其進行通信,那么創建線程有什么意義?
因為如果你這樣做,從中檢索任何狀態,無論是在終止時還是在其他任何時候,都將是一件小事,你甚至不會首先考慮使用期貨。

除了它的(有爭議的)便利性或審美吸引力之外,這些類型的技巧在功能上實現了什么,可以被認為足夠有用以抵消隱藏成本?

在沒有適當的多任務處理背景的情況下閱讀此類答案,街區的新孩子可能會因定期使用這些低效機制而受到誘惑。
那只會用更多的施萊米爾畫家綜合症來污染未來的軟件。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM