简体   繁体   English

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

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

I want to do something like this:我想做这样的事情:

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);
}

So I simply call SetMsg("mymessage");所以我简单地调用SetMsg("mymessage"); and regardless in which thread it is called from it prints properly.并且无论在哪个线程中调用它都可以正确打印。 The above would work except the UnicodeString gets deallocated before lambda is called.除了 UnicodeString 在调用UnicodeString之前被释放外,上面的方法都有效。

It is my understanding that the lambda above should remain allocated in this form, and it indeed works if I hardcode the string in the lambda itself instead of fMsg parameter.据我了解,上面的 lambda 应该保持以这种形式分配,如果我在 lambda 本身而不是 fMsg 参数中硬编码字符串,它确实有效。 So how do I pass the parameter then?那么我该如何传递参数呢?

I could allocate it with new and delete later, but that fails for some reason too.我可以用 new 分配它并稍后删除,但由于某种原因也失败了。

I tried:我试过了:

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);
}

Would it be faster (less expensive) or easier with the TThread::CreateAnonymousThread ?使用TThread::CreateAnonymousThread会更快(更便宜)还是更容易?

The first version works fine if TThread::Synchronize is used but I want to avoid thread blocking.如果使用TThread::Synchronize ,第一个版本工作正常,但我想避免线程阻塞。

The above would work except the UnicodeString gets deallocated before lambda is called.除了 UnicodeString 在调用UnicodeString之前被释放外,上面的方法都有效。

Your lambda is capturing the UnicodeString by reference , so when SetMsg() exits, the UnicodeString will be destroyed, leaving the reference inside the lambda dangling.您的UnicodeString正在通过引用捕获 UnicodeString,因此当SetMsg()退出时, UnicodeString将被销毁,留下 lambda 内的引用悬空。 The lambda needs to capture the UnicodeString by value instead to make its own copy . UnicodeString需要按值捕获 UnicodeString,而不是制作自己的副本

Also, I would not recommend using the global Form2 variable to call SetMsg() on, I would have the lambda capture SetMsg 's this pointer instead.此外,我不建议使用全局Form2变量来调用SetMsg() ,我会让 lambda 捕获SetMsgthis指针。

Try this:尝试这个:

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);
}

Alternatively, don't have the lambda call SetMsg() a second time, just access the Memo directly instead, since you know the GetCurrentThreadId() check will be redundant at that point:或者,不要让 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.我可以用new分配它并稍后delete ,但由于某种原因也失败了。

Your lambda is capturing the pointer by reference , so you are leaving the reference inside the lambda dangling when SetMsg() exits.您的 lambda 正在通过引用捕获指针,因此当SetMsg()退出时,您将 lambda 中的引用悬空。 You need to capture the pointer by value instead:您需要按值捕获指针:

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);
}

In general, using a dynamic UnicodeString will work, though I would strongly suggest capturing a std::unique_ptr or std::shared_ptr instead to handle the deallocation for you, instead of using a manual try/__finally block:通常,使用动态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 ?使用TThread::CreateAnonymousThread会更快(更便宜)还是更容易?

No. You are already running in a thread when calling Queue() , there is no reason to waste overhead spawning another thread.不。调用Queue()时您已经在一个线程中运行,没有理由浪费开销来生成另一个线程。 And you would still have to deal with copying values into that thread's lambda/callback function.而且您仍然需要处理将值复制到该线程的 lambda/回调 function 中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM