2

BrowserInteropHelperを使用してJavaScriptを介してユーザーにAPIを提供するWPFXBAPアプリケーションを開発しています。管理対象部分を新しいasync-await方式で書き換えた後、メソッド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;    
}
4

3 に答える 3

2

管理対象部分を新しいasync-await方式で書き換えた後、メソッドasync Task自体を呼び出さずに、非同期操作が終了するまで待機する必要があります。

これは、あなたがやろうとすることができる最も難しいことの1つです。

AsyncMethodは非同期Task<T>であり、アプリケーション内、つまりボタンハンドラー内から実行および待機されたときに完全に機能します。UIに依存するロジックがたくさんあります

そして、この状況はそれをほぼ不可能にします。


問題は、UIメッセージキューへのディスパッチをSyncMethod許可しながら、UIスレッドで実行されているものをブロックする必要があることです。AsyncMethod

あなたが取ることができるいくつかのアプローチがあります:

  1. またはのTask使用をブロックします。を使用すると、私のブログで説明されているデッドロックの問題を回避できます。つまり、UIに依存するロジックを、または他の非ブロッキングコールバックに置き換えるには、リファクタリングする必要があります。WaitResultConfigureAwait(false)AsyncMethodAsyncMethodIProgress<T>
  2. ネストされたメッセージループをインストールします。これは機能するはずですが、再入可能性の影響をすべて検討する必要があります。

個人的には、(1)の方がきれいだと思います。

于 2012-10-22T15:00:01.963 に答える
0

これを行う:

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()が呼び出されます。

于 2012-10-22T13:52:24.913 に答える
-1

これは私のためにそれを解決しました。

呼び出し側が完了するタスク遅延を追加します。

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;        
}
于 2018-11-12T14:47:28.417 に答える