尝试同步调用异步方法时发生死锁

3

我知道这个问题已经被问了很多次,但是我找不到适合我的答案。

我正在编写一个Xamarin Forms应用程序。在Android项目中,我需要重写这个方法:

public override bool OnJsConfirm(WebView view, string url, string message, JsResult result)

很遗憾,我不能将该方法设为异步。在该方法中,我想调用另外一个方法:
public static Task<bool> DisplayAlert(string title, string message, string accept, string cancel)
{
    var tcs = new TaskCompletionSource<bool>();

    Device.BeginInvokeOnMainThread(async () =>
    {
        var result = await MainPage.DisplayAlert(title, message, accept, cancel);
        tcs.SetResult(result);
    });

    return tcs.Task;
}

MainPage.DisplayAlert()需要在UI线程上调用,我的方法可以确保这一点。

我已经尝试从同步的OnJsConfirm方法中调用DisplayAlert,使用了以下变体:

//bool success = UI.DisplayAlert(title, message, ok, cancel).Result;                                // hangs !!!
//bool success = UI.DisplayAlert(title, message, ok, cancel).WaitAndUnwrapException();              // hangs !!! WaitAndUnwrapException is in Nito.AsyncEx.Synchronous
//bool success = Nito.AsyncEx.AsyncContext.Run(() => UI.DisplayAlert(title, message, ok, cancel));  // hangs !!!
//bool success = TaskEx.Run(() => UI.DisplayAlert(title, message, ok, cancel)).Result;              // hangs !!!
//bool success = Task.Run(() => UI.DisplayAlert(title, message, ok, cancel)).Result;                // hangs !!!
//bool success = Task.Run(async () => await UI.DisplayAlert(title, message, ok, cancel)).Result;    // hangs !!!
bool success = Task.Run(async () => await UI.DisplayAlert(title, message, ok, cancel).ConfigureAwait(false)).Result;    // hangs !!!

我采用了Stephen Cleary在一个类似问题的答案中提出的使用Nito.AsyncEx的建议。

我也已经尝试将.ConfigureAwait(false)添加到await MainPage.DisplayAlert(...)中,但这也没有起作用。

当我在await MainPage.DisplayAlert行设置断点时,在所有变体中都会触发,但当我按下F10时,VS2015停止在这个调用堆栈中:enter image description here

没有异常,应用程序只是挂起了。

有谁知道怎样调用我的异步方法吗?


看起来你正在DisplayAlert函数内部调用DisplayAlert函数,因此你将会一直无限循环调用它,导致应用程序挂起。 - Gordon Allocman
可能有效的一次尝试:new Handler().Post(new Runnable(async () => //code)); - William Barbosa
1个回答

3
OnJsConfirm 的返回值仅表示您是否处理确认对话框。因此,您需要返回 true 并将其余部分异步处理。如果您的 DisplayAlert 返回,则可以根据任务的结果在 JsResult 上调用 Confim() 或 Cancel()。请注意保留 HTML 标记。
public override bool OnJsConfirm(WebView view, string url, string message, JsResult result)
{
    DisplayAlert("Really?", message, "OK", "Cancel").ContinueWith(t => {
        if(t.Result) {
            result.Confirm();
        }
        else {
            result.Cancel();
        }
    });
    return true;
}


public static Task<bool> DisplayAlert(string title, string message, string accept, string cancel)
{
    var tcs = new TaskCompletionSource<bool>();

    Device.BeginInvokeOnMainThread(async () =>
    {
        var result = await MainPage.DisplayAlert(title, message, accept, cancel);
        tcs.SetResult(result);
    });

    return tcs.Task;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接