繁体   English   中英

从分派器上调用的异步操作返回值

[英]Returning value from async Action invoked on Dispatcher

我正在开发一个WPF XBAP应用程序,该应用程序通过使用BrowserInteropHelper通过JavaScript向用户提供API。 在以新的异步等待方式重写托管部分之后,需要等到异步操作完成后才能使调用方法async Task本身不进行调用。 我有 :

public string SyncMethod(object parameter)
{
    var sender = CurrentPage as MainView; // interop
    var result = string.Empty;
    if (sender != null)
    sender.Dispatcher.Invoke(DispatcherPriority.Normal,
                                 new Action(async () =>
                                                {
                                                    try
                                                    {
                                                        result = await sender.AsyncMethod(parameter.ToString());
                                                    }
                                                    catch (Exception exception)
                                                    {
                                                        result = exception.Message;
                                                    }
                                                }));
    return result;        
}

此方法返回string.Empty,因为它在执行后立即发生。 我试图将此操作分配给DispatcherOperation并执行while(dispatcherOperation.Status!= Completed){Wait(); },但即使执行后该值仍为空。 dispatcherOperation.Task.Wait()或dispatcherOperation.Task.ContinueWith()也没有帮助。
编辑
也尝试过

var op = sender.Dispatcher.BeginInvoke(...);
op.Completed += (s, e) => { var t = result; };

但是t也为空,因为赋值发生在等待异步方法之前。
编辑
长话短说,SyncMethod JavaScript互操作处理程序帮助程序包装器及其运行的所有操作必须在Dispatcher上。 AsyncMethod是异步Task <T>,当在应用程序内部(即在某些按钮处理程序内部)执行并等待时,可以完美地工作。 它具有许多依赖于UI的逻辑(显示状态进度栏,更改光标等)。
编辑
AsyncMethod:

public async Task<string> AsyncMethod(string input)
{
    UIHelpers.SetBusyState(); // overriding mouse cursor via Application.Current.Dispatcher
    ProgressText = string.Empty; // viewmodel property reflected in ui
    var tasksInputData = ParseInput(input);    
    var tasksList = new List<Task>();
    foreach (var taskInputDataObject in tasksInputData)
    {
        var currentTask = Task.Factory.StartNew(() =>
        {            
            using (var a = new AutoBusyHandler("Running operation" + taskInputData.OperationName))
            { // setting busy property true/false via Application.Current.Dispatcher
                var databaseOperationResult = DAL.SomeLongRunningMethod(taskInputDataObject);
                ProgressText += databaseOperationResult;
            }
        });
        tasksList.Add(insertCurrentRecordTask);
    }    
    var allTasksFinished = Task.Factory.ContinueWhenAll(tasksList.ToArray(), (list) =>
    {        
        return ProgressText;
    });
    return await allTasksFinished;    
}

在以新的异步等待方式重写托管部分之后,需要等到异步操作完成后才能使调用方法async Task本身不进行调用。

这是您可以尝试做的最困难的事情之一。

AsyncMethod是异步Task <T>,当在应用程序内部(即在某些按钮处理程序内部)执行并等待时,可以完美地工作。 它具有很多依赖于UI的逻辑

这种情况几乎是不可能的。


问题是,你需要阻止SyncMethod这是在UI线程上运行,同时允许AsyncMethod派遣到UI消息队列。

您可以采取几种方法:

  1. 使用WaitResult阻止Task 您可以通过在AsyncMethod使用ConfigureAwait(false)来避免博客上描述死锁问题 这意味着AsyncMethod必须进行重构,以取代相关的UI逻辑IProgress<T>或其他非阻塞回调。
  2. 安装嵌套的消息循环。 这应该可行,但是您必须考虑所有重新进入的分支。

我个人认为(1)更清洁。

做这个:

var are = new AutoResetEvent(true);
sender.Dispatcher.Invoke(DispatcherPriority.Normal,                                      
        new Action(async () =>
        {
           try { ... } catch { ... } finally { are.Set(); }
        }));



are.WaitOne();
return result;

are.WaitOne()将告诉AutoResetEvent等待,直到发出信号为止,这将在调度程序动作最终到达调用are.Set()的块时发生。

这为我解决了。

添加任务延迟以使调用者完成操作

public string SyncMethod(object parameter)  
{
    var sender = CurrentPage as MainView; // interop
    var result = string.Empty;

    if (sender != null)
    {
        await sender.Dispatcher.Invoke(DispatcherPriority.Normal,
            new Func<Task>(async () =>
            {
                try
                {
                    result = await sender.AsyncMethod(parameter.ToString());
                }
                catch (Exception exception)
                {
                    result = exception.Message;
                }
            }));
    }

    await Task.Delay(200); // <-- Here
    return result;        
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM