我有一个函数,它调用Concurrent::create_task在后台执行一些工作。在该任务中,需要调用StreamSocket
类上的connectAsync
方法,以便将套接字连接到设备。一旦连接了设备,我就需要获取一些对连接的套接字内部事物的引用(比如输入和输出流)。
由于这是一个异步方法,将返回一个IAsyncAction
,我需要在connectAsync
函数上创建另一个可以等待的任务。这在没有等待的情况下有效,但当我尝试在这个内部任务上使用wait()
进行错误检查时,会出现复杂情况。
Concurrency::create_task( Windows::Devices::Bluetooth::Rfcomm::RfcommDeviceService::FromIdAsync( device_->Id ) )
.then( [ this ]( Windows::Devices::Bluetooth::Rfcomm::RfcommDeviceService ^device_service_ )
{
_device_service = device_service_;
_stream_socket = ref new Windows::Networking::Sockets::StreamSocket();
// Connect the socket
auto inner_task = Concurrency::create_task( _stream_socket->ConnectAsync(
_device_service->ConnectionHostName,
_device_service->ConnectionServiceName,
Windows::Networking::Sockets::SocketProtectionLevel::BluetoothEncryptionAllowNullAuthentication ) )
.then( [ this ]()
{
//grab references to streams, other things.
} ).wait(); //throws exception here, but task executes
基本上,我已经发现,创建要连接的初始任务的同一个线程(可能是UI)也执行该任务和内部任务。每当我试图从外部任务调用内部任务的.wait()
时,我会立即得到一个异常。但是,内部任务将完成并成功连接到设备。
为什么我的异步链在UI线程上执行?我如何才能正确地等待这些任务?
通常,您应该避免.wait()
,而只是继续异步链。如果出于某种原因需要阻止,唯一的防傻机制就是从后台线程(例如,WinRT线程池)显式运行代码。
可以尝试使用.then()
重载,该重载接受task_options
并传递concurrency::task_options(concurrency::task_continuation_context::use_arbitrary())
,但不能保证继续将在另一个线程上运行;它只是说,如果它这样做就可以了——请参阅这里的文档。
您可以设置一个事件并让主线程等待它。我已经用一些IO异步操作完成了这一操作。下面是一个使用线程池、使用事件等待工作的基本示例:
TEST_METHOD(ThreadpoolEventTestCppCx)
{
Microsoft::WRL::Wrappers::Event m_logFileCreatedEvent;
m_logFileCreatedEvent.Attach(CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, WRITE_OWNER | EVENT_ALL_ACCESS));
long x = 10000000;
auto workItem = ref new WorkItemHandler(
[&m_logFileCreatedEvent, &x](Windows::Foundation::IAsyncAction^ workItem)
{
while (x--);
SetEvent(m_logFileCreatedEvent.Get());
});
auto asyncAction = ThreadPool::RunAsync(workItem);
WaitForSingleObjectEx(m_logFileCreatedEvent.Get(), INFINITE, FALSE);
long i = x;
}
这里有一个类似的例子,只是它包含了一点Windows运行时异步IO:
TEST_METHOD(AsyncOnThreadPoolUsingEvent)
{
std::shared_ptr<Concurrency::event> _completed = std::make_shared<Concurrency::event>();
int i;
auto workItem = ref new WorkItemHandler(
[_completed, &i](Windows::Foundation::IAsyncAction^ workItem)
{
Windows::Storage::StorageFolder^ _picturesLibrary = Windows::Storage::KnownFolders::PicturesLibrary;
Concurrency::task<Windows::Storage::StorageFile^> _getFileObjectTask(_picturesLibrary->GetFileAsync(L"art.bmp"));
auto _task2 = _getFileObjectTask.then([_completed, &i](Windows::Storage::StorageFile^ file)
{
i = 90210;
_completed->set();
});
});
auto asyncAction = ThreadPool::RunAsync(workItem);
_completed->wait();
int j = i;
}
我尝试使用事件来等待Windows运行时异步工作,但它被阻止了。这就是为什么我必须使用线程池。