我在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
答案 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);
}