2016-03-07 17 views
1

在WP7应用程序中,我使用命名的互斥锁来同步对StorageFiles和Tiles的访问。 使用UWP应用程序的异步代码时,这不再稳健,因为互斥体是线程仿射的并与异步代码混合,这会导致错误“对象同步方法是从未同步的代码块中调用的”。如何同步UWP应用程序与其后台任务之间的资源访问?

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

在这里不使用SemaphoreSlim,因为应用程序和后台任务运行在不同的进程中。

这个post建议使用Taks.Factory.StartNew与TaskCreationOptions.LongRunning或StaTaskScheduler。

LongRunning并未解决问题,因为我的测试代码证明了这一点,请参阅here。 我发现StaTaskScheduler的版本使用UWP中不可用的Thread类。

有人有一个解决方案,或至少一个UWP兼容版本的StaTaskScheduler - 在上述Noseratio提到“新线程”可以用Factory.StartNew替换。

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

回答

0

随着周年SDK同步的新独立的处理模块变得容易得多,因为后台处理不再在不同的进程中运行。我正在使用Stephen Clearey的AsyncEx中的AsyncLock。

原来的答案:

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

只要.ReleaseMutex与.WaitOne(例如.WaitOne)编码在同一水平上,我就得出结论认为互斥体在这种情况下可以正常工作。而不是在.WaitOne之后等待的异步方法内)。

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

'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 

另外一个可以使用文件锁定背出:

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 
0

如果我正确理解你,你正在寻找和异步锁定。看看http://asynclock.codeplex.com

+0

没有,我觉得有一个普遍的问题使用具有异步代码的命名互斥体。尽管你的链接看起来很有趣。但是,这些异步锁似乎不适用于x进程场景。 –

0

你得到的异常,因为你不与mutex.WaitOne()阻塞线程,这样你的互斥量没有信号,因此你的代码是不同步的,你会得到异常:

对象同步方法是从代码不同步块称为

尝试这样的:

Async Function TestMutex() As Task 
    Using mutex As New Threading.Mutex(False, "SyncAppAndBackTask") 
     Try 
      mutex.WaitOne() 
      For i = 1 To 3 
       Await SimulateCompute() 
       Debug.WriteLine(i) 
      Next 
     Catch ex As Exception 
      Debug.WriteLine(ex.Message) 
      Throw 
     Finally 
      mutex.ReleaseMutex() 
      Debug.WriteLine("success") 
     End Try 
    End Using 
End Function 

同样在你的代码中,你似乎正在使用TaskCountinuationOtions,无论是在StartNew它应该是TaskCreationOptions。虽然,无论是枚举和LongRunning等于2.

+0

感谢您的快速回复并修复了我的代码。省略互斥体.WaitOne()仅在缩短测试样本的生产代码时发生。有了这个修复程序,我的测试不再产生上述异常。 –

+0

现在我不能再重复这个例外。简单地使用互斥量而没有任何额外的措施似乎工作正常。我看到你发布了一个类似的问题 [与await命名互斥](http://stackoverflow.com/questions/23153155/named-mutex-with-await) 并解决了基于自定义TaskScheduler的解决方案。我不再保证同步异步文件IO与已命名的互斥体存在问题。你怎么看? –

+0

@PeterMeinl在UWP中直接线程管理是不可能的,因此我不确定现在如何使用自定义TaskScheduler的解决方案。尽管如我所见,* LongRunning *任务应该可行 - 它将一个任务专用为单线程,并且在等待返回之后应该可以安全地释放互斥锁。如果您的异步操作时间不长,您可能还会考虑在获取互斥锁之后进行同步等待而不是等待。最好的方法就是测试它 - 也许创建一个示例BTask,打开一个文件,并在主UI(与全局互斥)相同。 – Romasz