如何在 UWP 应用与其后台任务之间同步资源访问? [英] How to synchronize resource access between UWP app and its background tasks?

查看:21
本文介绍了如何在 UWP 应用与其后台任务之间同步资源访问?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 WP7 应用程序中,我使用命名的互斥锁来同步对 StorageFiles 和 Tiles 的访问.对于 UWP 应用的异步代码,这不再健壮,因为互斥体是线程仿射的,并且与异步代码混合,这会导致错误从未同步的代码块调用了对象同步方法".>

In WP7 apps I use a named Mutex to synchronize access to StorageFiles and Tiles. With the async code of UWP apps this is no longer robust because mutexes are thread-affine and mixed with async code this results in errors "Object synchronization method was called from an unsynchronized block of code".

Using mutex As New Threading.Mutex(False, "SyncAppAndBackTask")
    Try
        Await ...
    Finally
        mutex.ReleaseMutex()
    End Try
End Using

此处不建议使用 SemaphoreSlim,因为应用程序和后台任务在不同的进程中运行.

Using a SemaphoreSlim is not a option here because app and background tasks run in different processes.

这篇帖子 建议将 Taks.Factory.StartNew 与 TaskCreationOptions.LongRunning 或 StaTaskScheduler 一起使用.

This post suggests using Taks.Factory.StartNew with TaskCreationOptions.LongRunning or a StaTaskScheduler.

LongRunning 没有解决问题,正如我的测试代码所证明的那样,请参阅此处.我发现 StaTaskScheduler 的版本使用了 UWP 中没有的 Thread 类.

LongRunning does not solve the problem as my test code proves, see here. The versions of StaTaskScheduler I found use the Thread class which is not available in UWP.

是否有人对此有解决方案,或者至少有与 UWP 兼容的 StaTaskScheduler 版本 - 在上面的帖子中,Noseratio 提到可以使用 Factory.StartNew 替换新线程".

Does someone have a solution for this or at least a UWP compatible version of StaTaskScheduler - in the post above Noseratio mentions "new Thread" could be replaced using Factory.StartNew.

作为一种解决方法,我目前通过 .OpenAsync(FileAccessMode.ReadWrite) 使用存储文件锁,但这会导致难看的重试循环.

As a workaround I currently use a storage file lock via .OpenAsync(FileAccessMode.ReadWrite) but this leads to ugly retry-loops.

推荐答案

使用 Anniversary SDK 的新单进程模型,同步变得更加容易,因为后台处理不再在不同的进程中运行.我正在使用 Stephen Clearey 的 AsyncEx 中的 AsyncLock.

With the new Single Process Model of the Anniversary SDK syncing became much easier because background processing no longer runs in a different process. I am using the AsyncLock from Stephen Clearey's AsyncEx with it.

原答案:

诸如互斥体是线程仿射的,因此它们不能与异步代码一起使用"之类的语句以及我的应用程序和测试中的错误让我怀疑我们通常不能使用命名的互斥体进行同步UWP 应用与其后台任务之间的资源访问.我看到的所有替代解决方案都只在进程中起作用.

Statements like "Mutexes are thread-affine, so they don't work with async code" and bugs in my apps and tests had made me suspicious that we generally cannot use a named Mutex to synchronize resource access between an UWP app and its background tasks. All alternative solutions I saw only work in-process.

我得出的结论是,只要 .ReleaseMutex 与 .WaitOne 编码在同一级别(例如,不在 .WaitOne 之后等待的异步方法中),互斥锁在这种情况下确实可以正常工作.

I came to the conclusion that mutexes do work fine in this scenario as long as .ReleaseMutex is coded on the same level as .WaitOne (e.g. not within an async method awaited after .WaitOne).

为了编码方便,我封装了互斥量处理以允许使用语句:

For coding convenience I encapsulated the mutex handling to allow Using statements:

'Usage to serialize access:
Using New AppAndBackgroundMutex(TimeSpan.FromSeconds(5))
    'Access storage files
    'Access ApplicationData settings
    'Update Tiles
End Using

'Usage to back out:
Using New AppAndBackgroundMutex(TimeSpan.Zero)
    '...
End Using

Public NotInheritable Class AppAndBackgroundMutex : Implements IDisposable
    Private _mutex As Threading.Mutex
    Private _iOwnMutex As Boolean
    Sub New(waitTimeout As TimeSpan, Optional syncId As String = "SyncRates&Selections&Date")
        Const UniqePartOfMutexName = "<app specific GUID>"
        Try
            _mutex = New Threading.Mutex(False, UniqePartOfMutexName & syncId)
            _iOwnMutex = _mutex.WaitOne(waitTimeout)
            If Not _iOwnMutex Then
                Dim msg = ($"Unable to acquire mutex for app/background sync after waiting for {waitTimeout}.")
                If waitTimeout = TimeSpan.Zero Then
                    'Intentionally backing out
                    Trace.Info(msg)
                Else
                    Trace.Error(msg)
                End If
                Throw New MutexTimeoutException(msg)
            End If
        Catch ex As Threading.AbandonedMutexException
            Trace.Error("Abandoned Mutex detected! OS might have killed background task. Ignoring problem.")
            _iOwnMutex = True
        End Try
    End Sub

    'Simple Dispose implementaion because class is sealed
    Public Sub Dispose() Implements IDisposable.Dispose
        If _iOwnMutex Then _mutex.ReleaseMutex()
        _ mutex.Dispose()
    End Sub
End Class

或者可以使用文件锁来退出:

Alternatively one could use a file lock to back out:

Try
    Dim file = Await ApplicationData.Current.LocalFolder.CreateFileAsync("__AppAndBackgroundSync.lock", CreationCollisionOption.OpenIfExists)
    Await file.OpenAsync(FileAccessMode.ReadWrite)
    '...
Catch ex As UnauthorizedAccessException
    Throw New AppAndBackgroundConcurrencyViolationException()
End Try

这篇关于如何在 UWP 应用与其后台任务之间同步资源访问?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆