[C++] - lambda capture的成員函數 異步調用

關於lambda capture:

If the capture-default is &, subsequent simple captures must not begin with &.

struct S2 { void f(int i); };
void S2::f(int i)
{
    [&]{};          // OK: by-reference capture default
    [&, i]{};       // OK: by-reference capture, except i is captured by copy
    [&, &i] {};     // Error: by-reference capture when by-reference is the default
    [&, this] {};   // OK, equivalent to [&]
    [&, this, i]{}; // OK, equivalent to [&, i]
}

If the capture-default is =, subsequent simple captures must begin with & or be *this (since C++17) or this (since C++20).

struct S2 { void f(int i); };
void S2::f(int i)
{
    [=]{};          // OK: by-copy capture default
    [=, &i]{};      // OK: by-copy capture, except i is captured by reference
    [=, *this]{};   // until C++17: Error: invalid syntax
                    // since c++17: OK: captures the enclosing S2 by copy
    [=, this] {};   // until C++20: Error: this when = is the default
                    // since C++20: OK, same as [=]
}

Any capture may appear only once:

struct S2 { void f(int i); };
void S2::f(int i)
{
    [i, i] {};        // Error: i repeated
    [this, *this] {}; // Error: "this" repeated (C++17)
}

如果lambda capture的是成員函數,這個lambda在它所capture的類對象釋放後異步調用:

class A:
{
public:
    int func()
    {
        std::ofstream myfile("example.txt");
        if (myfile.is_open())
        {
            myfile << "Write from child thread.\n";
            myfile.close();
        }
        else
        {
            std::cout << "Unable to open file";
        }
    }

    void detach()
    {
        std::thread t([this]() {
            std::this_thread::sleep_for(std::chrono::milliseconds(3000));
            func();
        });
        t.detach();
    }
};

int main()
{
    {
        A a;
        a.detach();
    }

    std::cout << "main end" << std::endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(5000));
    return 0;
}

當main函數執行到打印“main end”時,a對象已經釋放,但是它創建的子線程中調用了它的成員函數依然可以正常執行。這是因爲成員函數在轉變爲彙編代碼時與普通的函數一樣,只不過會將this指針作爲它的第一個參數傳入,而且a對象的func()方法中沒有調用A的其他成員變量,因此不會發生crash。正確的寫法應該是在進入lambda的時候判斷此時它capture的this指針所指向的對象是否還是有效的:

#define RETURN_FROM_LAMBDA_IF_DEAD(x) \
    auto sharedThis = x.lock(); \
    if(!sharedThis) \
        return;

class A: public std::enable_shared_from_this<A>
{
public:
    int func()
    {
        std::ofstream myfile("example.txt");
        if (myfile.is_open())
        {
            myfile << "Write from child thread.\n";
            myfile.close();
        }
        else
        {
            std::cout << "Unable to open file";
        }
    }

    void detach()
    {
        std::thread t([weakThis = weak_from_this(), this]() {
            RETURN_FROM_LAMBDA_IF_DEAD(weakThis);
            std::this_thread::sleep_for(std::chrono::milliseconds(3000));
            func();
        });
        t.detach();
    }
};

int main()
{
    {
        A a;
        a.detach();
    }

    std::cout << "main end" << std::endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(5000));
    return 0;
}

另外,如果確實想要在lambda中調用this指向對象的成員函數且這個成員函數沒有調用成員變量,可以把這個成員函數定義成private static,這樣就不會有上面說的問題。

 

參考鏈接:

https://en.cppreference.com/w/cpp/language/lambda

https://stackoverflow.com/questions/57654701/member-function-captured-by-lambda-asynchronously-dispatch-issue

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