【问题标题】:winrt c++/cx concurrency access violation exceptionwinrt c++/cx 并发访问冲突异常
【发布时间】:2016-08-05 10:59:40
【问题描述】:

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

代码如下:

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

【问题讨论】:

  • 我也尝试过使用 WinRT 特定的扩展 (IAsyncOperation),在这种情况下它可以正常工作。
  • 为什么要在 fileVar 变量中捕获文件的值?你能不能只对 lambda 本身的文件做任何你需要做的处理?另外,我不明白您对 IAsyncOperation 的评论。您能粘贴适合您的代码吗?

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


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

    这是我整理的一个解决方案。有两件事需要了解:

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

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

    这篇文章的更多信息: 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);
                    });
                });
            });
        }
    });
    }
    

    【讨论】:

      猜你喜欢
      • 2012-11-14
      • 1970-01-01
      • 2015-12-08
      • 2022-01-07
      • 2016-03-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多