在我的 Xamarin.Forms 應用程式中,我有一些看起來像這樣的代碼
private async void OnEntryLosesFocus(object sender, EventArgs e) {
var vm = (MainPageViewModel)BindingContext;
if (vm == null)
{
return;
}
if (!IsAnyNavButtonPressedOnUWP())
{
// CheckAndSaveData() causes dialogs to show on the UI.
// I need to ensure that CheckAndSaveData() completes its execution and control is not given to another function while it is running.
var _saveSuccessfulInUnfocus = await vm.CheckAndSaveData();
if (_saveSuccessfulInUnfocus)
{
if (Device.RuntimePlatform == Device.UWP)
{
if (_completedTriggeredForEntryOnUWP)
{
var navAction = vm.GetNavigationCommand();
navAction?.Invoke();
_completedTriggeredForEntryOnUWP = false;
}
}
else
{
vm._stopwatchForTap.Restart();
}
}
else
{
vm._stopwatchForTap.Restart();
}
}
}
上面的方法是EventHandler針對我的一個條目的 unfocus 事件。但是,由于單擊按鈕時 Xamarin 的作業方式,在執行附加的命令之前觸發了 Unfocused 事件。
我需要確保函式vm.CheckAndSaveData()在附加到此按鈕的命令之前完成執行,因此我需要同步運行它。
我嘗試了多種方法,但它們都導致死鎖。
我嘗試過的一些解決方案在這個問題中:如何同步運行異步 Task<T> 方法?
它們都導致僵局。必須有某種方法可以同步運行我的函式,或者至少強制函式CheckAndSaveData在其他任何事情之前完成。
uj5u.com熱心網友回復:
恭喜,您找到了沒有解決方案的極端案例之一。在某些情況下,有一些用于異步同步的技巧,但這里沒有解決方案。
這就是為什么沒有已知的解決方案會起作用的原因:
- 您不能直接阻止,因為
CheckAndSaveData需要運行 UI 回圈。 - 您不能阻塞執行緒池執行緒,因為
CheckAndSaveData它與 UI 互動。 - 您不能使用替換單執行緒
SynchronizationContext(如在錯誤鏈接的副本中),因為CheckAndSaveData需要泵送 Win32 訊息。 - 您甚至不能使用嵌套的 UI 回圈(在這種情況下為 Dispatcher Frame),因為泵送這些相同的 Win32 訊息將導致您的命令被執行。
更簡單地說:
CheckAndSaveData必須泵送訊息以顯示 UI。CheckAndSaveData無法泵送訊息以阻止命令執行。
所以,這里沒有解決辦法。
相反,您需要修改您的命令,以便它以CheckAndSaveData某種方式等待,或者使用同步原語,或者CheckAndSaveData直接從命令中呼叫。
uj5u.com熱心網友回復:
一個選項是SemaphoreSlim在您的 unfocus 事件和您的命令事件中使用,以使命令事件等到 unfocus 事件完成。
當您的代碼的一部分進入信號量時(使用Waitor WaitAsync),它將阻止您的代碼中試圖進入信號量的其他部分,直到信號量被釋放。
這是一個可能看起來像的示例:
private static readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1,1);
private async void OnEntryLosesFocus(object sender, EventArgs e) {
var vm = (MainPageViewModel)BindingContext;
if (vm == null)
{
return;
}
if (!IsAnyNavButtonPressedOnUWP())
{
try {
await _semaphore.WaitAsync();
// CheckAndSaveData() causes dialogs to show on the UI.
// I need to ensure that CheckAndSaveData() completes its execution and control is not given to another function while it is running.
_saveSuccessfulInUnfocus = await vm.CheckAndSaveData();
if (_saveSuccessfulInUnfocus)
{
if (Device.RuntimePlatform == Device.UWP)
{
if (_completedTriggeredForEntryOnUWP)
{
var navAction = vm.GetNavigationCommand();
navAction?.Invoke();
_completedTriggeredForEntryOnUWP = false;
}
}
else
{
vm._stopwatchForTap.Restart();
}
}
else
{
vm._stopwatchForTap.Restart();
}
}
finally
{
_semaphore.Release();
}
}
}
private async void OnCommand(object sender, EventArgs e) {
try {
// Execution will wait here until Release() is called in OnEntryLosesFocus
await _semaphore.WaitAsync();
// do stuff
}
finally
{
_semaphore.Release();
}
}
try/塊是可選的finally,但它有助于確保即使發生未處理的例外也釋放信號量。
uj5u.com熱心網友回復:
使用資訊表單提供的答案以及此問題的幫助解決了這個問題
這就是我的做法。
private async void OnEntryLosesFocus(object sender, EventArgs e) {
var vm = (MainPageViewModel)BindingContext;
if (vm == null)
{
return;
}
// A public variable in my view model which is initially set to null.
vm.UnfocusTaskCompletionSource = new TaskCompletionSource<bool>();
var saveSuccessful = await vm.CheckAndSaveData();
vm.UnfocusTaskCompletionSource.SetResult(saveSuccessful);
if (saveSuccessful && Device.RuntimePlatform == Device.UWP &&
_completedTriggeredForEntryOnUWP)
{
var navAction = vm.GetNavigationCommand();
navAction?.Invoke();
_completedTriggeredForEntryOnUWP = false;
}
/*Set this back to null. (This is very important for my use case.). Just in case this function executes completely before the Button click command executes for whatever reason */
vm.UnfocusTaskCompletionSource = null;
}
在我Command附加到按鈕的功能中,我有如下內容
private async void OnButtonTap(ArrorDirection direction)
{
bool preventMove = false;
if (UnfocusTaskCompletionSource != null)
{
/*Wait for `CheckAndSaveData() from `OnEntryLoosesFocus()` to complete and get the return value. */
await UnfocusTaskCompletionSource.Task;
var dataCheckAndSaveSuccessful = UnfocusTaskCompletionSource.Task.Result;
preventMove = !dataCheckAndSaveSuccessful;
}
// Do stuff in function:
if (preventMove){
DoMove()
}
UnfocusTaskCompletionSource = null;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/413150.html
標籤:
