winrt c ++ / cx并发访问冲突异常

时间:2014-08-24 18:27:18

标签: concurrency c++-cx winrt-async

我要做的是检查本地文件夹中是否存在文件,然后将其复制到那里(如果找不到该文件(该文件以前作为资产添加到项目中)。

以下是代码:

Windows::Storage::StorageFile^ MainPage::GetCustomFileAsync(Platform::String^ fileName)
{
using Windows::Storage::StorageFile;
using Windows::Storage::StorageFolder;

auto localFolder = Windows::Storage::ApplicationData::Current->LocalFolder;
auto localTask = concurrency::create_task(localFolder->GetFileAsync(fileName));
StorageFile^ retVal = nullptr;
localTask.then([&](StorageFile^ t){
    retVal = t;
}).then([](concurrency::task<void> t)
{
    try
    {
        t.get();
        OutputDebugString(L"Found\n");
    }
    catch (Platform::COMException^ e)
    {
        OutputDebugString(e->Message->Data());
    }
}).wait();
return retVal;
}


StorageFile^ fileVar;
if ((fileVar = this->GetCustomFileAsync("somefile.txt")) == nullptr)
{
    String^ path = Windows::ApplicationModel::Package::Current->InstalledLocation->Path + "\\Assets";
    concurrency::create_task(Windows::Storage::StorageFolder::GetFolderFromPathAsync(path)).then([](StorageFolder^ folder){
        return (folder->GetFileAsync("somefile.txt"));
    }).then([](StorageFile^ file){
        return (file->CopyAsync(Windows::Storage::ApplicationData::Current->LocalFolder));
    }).then([&](StorageFile^ file){
        fileVar = file;
        OutputDebugString(file->DisplayName->Data());
    });

}

在“file”被分配给“fileVar”的时候,我得到了一个访问冲突异常(因为可能是跨线程访问?)。如何解决这个问题?

编辑:我无法在那里进行所有处理,因为文件将被多次访问。简而言之,我需要知道它何时被成功复制并获得它的处理。这是适用的代码

Windows::Storage::StorageFile^ GetFile(Platform::String^ fileName)
{
using Windows::Storage::StorageFile;
using Windows::Storage::StorageFolder;
using Windows::Foundation::AsyncOperationCompletedHandler;
using Windows::Foundation::AsyncStatus;
using Windows::Foundation::IAsyncOperation;
using Platform::String;

auto localFolder = Windows::Storage::ApplicationData::Current->LocalFolder;
bool completed = false;
StorageFile^ retVal = nullptr;
localFolder->GetFileAsync(fileName)->Completed = ref new AsyncOperationCompletedHandler<StorageFile^>([&completed, &retVal, &fileName](IAsyncOperation<StorageFile^>^ fileOperation, AsyncStatus status)
{
    if (status == AsyncStatus::Error)
    {
        String^ path = Windows::ApplicationModel::Package::Current->InstalledLocation->Path + "\\Assets";
        Windows::Storage::StorageFolder::GetFolderFromPathAsync(path)->Completed = ref new AsyncOperationCompletedHandler<Windows::Storage::StorageFolder^>(
            [&completed, &retVal, &fileName](IAsyncOperation<Windows::Storage::StorageFolder^>^ folderOperation, AsyncStatus status)->void{
            auto assetFolder = folderOperation->GetResults();
            assetFolder->GetFileAsync(fileName)->Completed = ref new AsyncOperationCompletedHandler<Windows::Storage::StorageFile^>([&completed, &retVal, &fileName](IAsyncOperation<Windows::Storage::StorageFile^>^ fileOperation, AsyncStatus status)->void{
                auto file = fileOperation->GetResults();
                file->CopyAsync(Windows::Storage::ApplicationData::Current->LocalFolder)->Completed = ref new AsyncOperationCompletedHandler<Windows::Storage::StorageFile^>
                    ([&completed, &retVal, &fileName](IAsyncOperation<Windows::Storage::StorageFile^>^ fileOperation, AsyncStatus status)->void {
                    retVal = fileOperation->GetResults();
                    completed = true;
                });
            });
        });
    }
    else
    {
        retVal = fileOperation->GetResults();
        completed = true;
    }
});
while (completed == false);
return retVal;
}

2 个答案:

答案 0 :(得分:1)

不是将委托作为参数传递并返回void,而是让您的方法返回task<StorageFile^>,然后调用者可以执行.then()以在操作成功后继续工作。

或者,如果将其公开为公共WinRT方法(不是内部/私有C ++方法),则使用IAsyncOperation<StorageFile^>^作为返回类型,并将整个内容包装在create_async()中:

IAsyncOperation<StorageFile^>^ DoStuff(params)
{
  return concurrency::create_async([params]
  { 
    // function body goes here
  });
}

答案 1 :(得分:0)

这是我整理的解决方案。要知道的两件事情很重要:

  1. 使用concurrency :: create_task执行异步操作时,当父函数返回时,异步操作仍然可以执行。因此捕获的变量必须比父函数的上下文更长。如果通过引用传递它们,显然不会发生这种情况。花了一段时间才意识到这一点。

  2. WinRT对并发运行时施加了某些限制。调用concurrency :: task :: get()或concurrency :: task :: wait()将在STA线程中抛出异常,除非调用是在任务延续中。

  3. 此帖中的更多信息: http://social.msdn.microsoft.com/Forums/windowsapps/en-US/ae54980b-41ce-4337-a059-2213b549be4b/concurrencyinvalidoperation-when-calling-tasktget?forum=winappswithnativecode

    在这种情况下如何知道功能何时完成它的工作?我选择传入回调(AKA代表)。

    delegate void FileOperation(Windows::Storage::StorageFile^ file);
    void GetFileConcurrency(Platform::String^ fileName, FileOperation^ fileOp)
    {
    using Windows::Storage::StorageFile;
    using Windows::Storage::StorageFolder;
    using Platform::String;
    
    auto localFolder = Windows::Storage::ApplicationData::Current->LocalFolder;
    String^ assetFolderPath = Windows::ApplicationModel::Package::Current->InstalledLocation->Path + "\\Assets";
    
    
    auto localFolderTask = concurrency::create_task(localFolder->GetFileAsync(fileName));
    localFolderTask.then([localFolder, assetFolderPath, fileName, fileOp](concurrency::task<StorageFile^> theTask){
        try
        {
            StorageFile^ theFile = theTask.get();
            fileOp(theFile);
        }
        catch (Platform::Exception^ e)
        {
            OutputDebugString(e->Message->Data());
            auto assetFolderTask = concurrency::create_task(StorageFolder::GetFolderFromPathAsync(assetFolderPath));
            assetFolderTask.then([localFolder, assetFolderPath, fileName, fileOp](StorageFolder^ assetFolder){
                auto assetFileTask = concurrency::create_task(assetFolder->GetFileAsync(fileName));
                assetFileTask.then([localFolder, assetFolderPath, fileName, fileOp](StorageFile^ file){
                    auto copyFileTask = concurrency::create_task(file->CopyAsync(localFolder));
                    copyFileTask.then([localFolder, assetFolderPath, fileName, fileOp](StorageFile^ file){
                        OutputDebugString(file->Path->Data());
                        fileOp(file);
                    });
                });
            });
        }
    });
    }