在 C++ Builder 中使用 lambda 将参数传递给 TThread::Queue

Passing parameters to TThread::Queue using lambda in C++ Builder

我想做这样的事情:

void TForm2::SetMsg(UnicodeString fMsg)
{
// If thread is not main Queue...
if (GetCurrentThreadId() != System::MainThreadID)
    {
    TThread::Queue( nullptr, [&]() -> void { Form2->SetMsg(fMsg); } );
    return;
    }

// If thread is main then print directly...
Memo1->Lines->Add(fMsg);
}

所以我只是调用 SetMsg("mymessage"); 并且无论在哪个线程中调用它都会正确打印。除了 UnicodeString 在调用 lambda 之前被释放外,上面的方法是可行的。

据我了解,上面的 lambda 应该保持这种形式分配,如果我在 lambda 本身而不是 fMsg 参数中硬编码字符串,它确实有效。那我怎么传参呢?

我可以用 new 分配它,然后再用 delete 分配它,但由于某种原因也失败了。

我试过了:

void TForm2::SetMsg(UnicodeString fMsg)
{
// If thread is not main Queue...
if (GetCurrentThreadId() != System::MainThreadID)
    {
    UnicodeString* p = new UnicodeString(fMsg);
    TThread::Queue( nullptr, [&]() -> void { try { Form2->SetMsg(*p); } __finally { delete p; } }  );
    return;
    }

// If thread is main then print directly...
Memo1->Lines->Add(fMsg);
}

使用 TThread::CreateAnonymousThread 会更快(更便宜)还是更容易?

如果使用 TThread::Synchronize,第一个版本工作正常,但我想避免线程阻塞。

The above would work except the UnicodeString gets deallocated before lambda is called.

您的 lambda 正在通过引用 捕获 UnicodeString ,因此当 SetMsg() 退出时,UnicodeString 将被销毁,留下引用在 lambda 内部悬空。 lambda 需要通过值 捕获 UnicodeString 而不是制作自己的 copy.

此外,我不建议使用全局 Form2 变量来调用 SetMsg(),我会使用 lambda 捕获 SetMsgthis 指针。

试试这个:

void TForm2::SetMsg(UnicodeString fMsg)
{
    // If thread is not main Queue...
    if (GetCurrentThreadId() != System::MainThreadID)
    {
        TThread::Queue( nullptr, [this, fMsg]{ this->SetMsg(fMsg); } );
        return;
    }

    // If thread is main then print directly...
    Memo1->Lines->Add(fMsg);
}

或者,不要第二次调用 lambda 调用 SetMsg(),只需直接访问 Memo,因为您知道 GetCurrentThreadId() 检查此时将是多余的:

void TForm2::SetMsg(UnicodeString fMsg)
{
    // If thread is not main Queue...
    if (GetCurrentThreadId() != System::MainThreadID)
    {
        TThread::Queue( nullptr, [this, fMsg]{ this->Memo1->Lines->Add(fMsg); } );
        return;
    }

    // If thread is main then print directly...
    Memo1->Lines->Add(fMsg);
}

I could allocate it with new and delete later, but that fails for some reason too.

您的 lambda 正在通过引用 捕获指针 ,因此当 SetMsg() 退出时,您将 lambda 中的引用悬空。你需要通过值来捕获指针而不是:

void TForm2::SetMsg(UnicodeString fMsg)
{
    // If thread is not main Queue...
    if (GetCurrentThreadId() != System::MainThreadID)
    {
        UnicodeString* p = new UnicodeString(fMsg);
        TThread::Queue( nullptr, [this, p]{ try { this->SetMsg(*p); /* or: this->Memo1->Lines->Add(*p); */ } __finally { delete p; } } );
        return;
    }

    // If thread is main then print directly...
    Memo1->Lines->Add(fMsg);
}

一般来说,使用动态 UnicodeString 会起作用,但我强烈建议捕获 std::unique_ptrstd::shared_ptr 来为您处理重新分配,而不是使用手动try/__finally块:

void TForm2::SetMsg(UnicodeString fMsg)
{
    // If thread is not main Queue...
    if (GetCurrentThreadId() != System::MainThreadID)
    {
        // using unique_ptr, must be moved into the lambda
        auto p = std::make_unique<UnicodeString>(fMsg);
        TThread::Queue( nullptr, [this, p = move(p)]{ this->SetMsg(*p); /* or: this->Memo1->Lines->Add(*p); */ } );

        // using shared_ptr, can be copied by value
        auto p = std::make_shared<UnicodeString>(fMsg);
        TThread::Queue( nullptr, [this, p]{ this->SetMsg(*p); /* or: this->Memo1->Lines->Add(*p); */ } );

        return;
    }

    // If thread is main then print directly...
    Memo1->Lines->Add(fMsg);
}

Would it be faster (less expensive) or easier with the TThread::CreateAnonymousThread?

没有。调用 Queue() 时,您已经 运行 在一个线程中,没有理由浪费开销来生成另一个线程。而且您仍然需要处理将值复制到该线程的 lambda/callback 函数中的问题。