使用std :: async引用对象

时间:2017-11-07 10:16:07

标签: c++

我在visual studio 2012中有一个小解决方案。该解决方案包含两个项目(Scanner和TestApp),Scanner是一个dll,TestApp是一个使用dll的小应用程序。 我希望dll中的一个函数在一个线程中运行,并通过队列将其结果报告给TestApp。 为了测试这个,我写了一个最小的应用程序,但根据我如何启动线程,我得到不同的结果,我想了解原因。

Scanner.h 文件如下所示:

#pragma once

#include <iostream>
#include <string>
#include <stdint.h>
#include <atomic>
#include <future>
#include <thread>

#include "version.h"
#include "threadsafe_queue.h"
#include "capture_data.h"
#include "process_data.h"


#ifdef SCANNER_EXPORTS  
#define SCANNER_API __declspec(dllexport)   
#else  
#define SCANNER_API __declspec(dllimport)   
#endif  

class Scanner
{
public:
    static SCANNER_API void run();  
    static SCANNER_API void stop(); 
};

Scanner.cpp:

#include "stdafx.h"
#include "Scanner.h"

std::vector<std::future<int>> my_futures;

void Scanner::run()
{
    CaptureData capture_data(1234);
    auto t = std::async(std::launch::async, &CaptureData::get_data, capture_data);
    my_futures.push_back(std::move(t));
}

void Scanner::stop()
{
    for(int n=0; n<my_futures.size(); n++) {
    auto e = std::move(my_futures.back());
    e.get();
    my_futures.pop_back();
    }
}

CaptureData类在capture_data.h和capture_data.cpp中定义。

capture_data.h:

#pragma once

#include <atomic>
#include <thread>
#include "iq_data.h"
#include "threadsafe_queue.h"

class CaptureData
{
public:
    CaptureData(double freq_start);
    void configure();
    void get_data();
private:
    double m_test;
};

capture_data.cpp

#include "stdafx.h"
#include "capture_data.h"
#include "Scanner.h"

ThreadsafeQueue<int> g_queue_1;
SCANNER_API ThreadsafeQueue<int> g_queue_2;


CaptureData::CaptureData(double test) 
    : m_test(test) {}

void CaptureData::get_data() 
{
    cout << "1: " << m_test << std::endl;
    Sleep(5000);
    cout << "2: " << m_test << std::endl;   
    g_queue_2.push(3);
    cout << "Capture has now pushed data" << std::endl;
}

最后是 TestApp.cpp

#include "stdafx.h"
#include "tchar.h"
#include <stdint.h>
#include <string>
#include "Scanner.h"


SCANNER_API extern ThreadsafeQueue<int> g_queue_2;

int _tmain(int argc, _TCHAR* argv[])
{
    Scanner scanner;
    scanner.run();
    cout << "TestApp waiting for data..." << std::endl;
    int data;
    g_queue_2.wait_and_pop(data);
    cout << "TestApp got data: " << data << std::endl;
    scanner.stop();

    return 0;
}

在Scanner.cpp中,我尝试以两种不同的方式启动线程,第一种方式:

auto t = std::async(std::launch::async, &CaptureData::get_data, capture_data);

第二种方式是引用对象“capture_data”:

auto t = std::async(std::launch::async, &CaptureData::get_data, &capture_data);

第一种方式似乎有效,因为我打算让应用程序工作,我在终端中获得以下打印输出:

TestApp waiting for data... 
1: 1234 
2: 1234 
Capture has now pushed data 
TestApp got data: 3 
Press any key to continue...

如果我使用第二种方式:

TestApp waiting for data... 
1: 6.95166e-310
2: 6.95166e-310 
Capture has now pushed data 
TestApp got data: 3 
Press any key to continue...

所以,我不明白为什么变量“m_test”在第二种情况下搞砸了。 如果有人能够对此有所了解,我将非常感激。

/ M

2 个答案:

答案 0 :(得分:2)

在以下代码中:

void Scanner::run()
{
    CaptureData capture_data(1234);
    auto t = std::async(std::launch::async, &CaptureData::get_data, capture_data);
    my_futures.push_back(std::move(t));

}

capture_data是一个局部变量,超出范围并在函数返回时被销毁。如果将指向该变量的指针传递给async,则指针变为悬空指针,从而导致未定义的行为。如果您按值传递它,就不会发生这种情况,就像在上面的代码段中那样。

答案 1 :(得分:0)

您正在尝试将指针传递给堆栈分配的对象。此对象在Scanner::run()方法的末尾被破坏。因此,当异步函数运行时,指针现在指向无效的内存。

第一种方法有效,因为capture_data变量在传递给函数时是移动构造的,因此它仍然保留了它的结构。

我建议使用lambda函数而不是传递原始成员函数:

void Scanner::run()
{
    CaptureData capture_data(1234);
    auto t = std::async(std::launch::async, [capture=std::move(capture_data)]() { capture.get_data(); });
    my_futures.emplace_back(t);
}

更好的是在lambda函数中构造对象:

void Scanner::run()
{
    auto t = std::async(std::launch::async, []() { 
        CaptureData capture_data(1234);
        capture_data.get_data();
    });
    my_futures.emplace_back(t);
}