2016-03-07 43 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