25

await変数を true に設定するメソッドを書きたいと思います。

これが疑似コードです。

bool IsSomethingLoading = false
SomeData TheData;

public async Task<SomeData> GetTheData()
{
   await IsSomethingLoading == true;
   return TheData;
}

TheDataIsSomethingLoading変数とともに Prism イベントによって設定されます。

メソッドへの呼び出しがありますが、GetTheData非同期で実行したいと思います (現在、データの準備ができていない場合は null を返すだけです。(これは他の問題につながります)。)

これを行う方法はありますか?

4

4 に答える 4

25

このような多くの状況で必要なのはTaskCompletionSource.

ある時点でデータを生成できるメソッドがある可能性がありますが、それを行うためにタスクを使用しません。おそらく、結果を提供するコールバックを受け取るメソッド、または結果があることを示すために発生するイベント、または単に を使用するコードThreadまたはThreadPoolを使用するようにリファクタリングする傾向がないメソッドが存在する可能性がありますTask.Run

public Task<SomeData> GetTheData()
{
    TaskCompletionSource<SomeData> tcs = new TaskCompletionSource<SomeData>();
    SomeObject worker = new SomeObject();
    worker.WorkCompleted += result => tcs.SetResult(result);
    worker.DoWork();
    return tcs.Task;
}

ワーカーや他のクラスにを提供する必要がある/提供したい場合TaskCompletionSource、または他の方法でより広い範囲に公開する必要がある場合がありますが、適切な場合は非常に強力なオプションであるにもかかわらず、必要ではないことがよくあります。

を使用Task.FromAsyncして、非同期操作に基づいてタスクを作成し、そのタスクを直接返すかawait、コードで返すこともできます。

于 2013-02-27T21:47:58.267 に答える
16

TaskCompletionSourceをシグナルとして使用できますawait

TaskCompletionSource<bool> IsSomethingLoading = new TaskCompletionSource<bool>();
SomeData TheData;

public async Task<SomeData> GetTheData()
{
   await IsSomethingLoading.Task;
   return TheData;
}

そして、Prism イベントで次のことを行います。

IsSomethingLoading.SetResult(true);
于 2013-02-27T21:48:55.310 に答える
0

非常に単純な解決策を提案しますが、速度のパフォーマンスを考慮していない場合、元の質問に答えるのに最適ではありません。

...
public volatile bool IsSomethingLoading = false;
...
public async Task<SomeData> GetTheData()
{
    // Launch the task asynchronously without waiting the end
    _ = Task.Factory.StartNew(() =>
    {
        // Get the data from elsewhere ...
    });

    // Wait the flag    
    await Task.Factory.StartNew(() =>
    {
        while (IsSomethingLoading)
        {
            Thread.Sleep(100);
        }
    });

   return TheData;
}

重要な注意: @Theodor Zoulias の提案:キーワードIsSomethingLoadingを使用して宣言する必要があります。これvolatileにより、コンパイラの最適化と、他のスレッドからアクセスする際の潜在的なマルチスレッドの問題を回避できます。コンパイラーの最適化の詳細については、次の記事に従ってください: The C# Memory Model in Theory and Practice

以下に完全なテストコードを追加しています:

XAML :

<Label x:Name="label1" Content="Label" HorizontalAlignment="Left" Margin="111,93,0,0" VerticalAlignment="Top" Grid.ColumnSpan="2" Height="48" Width="312"/>

テストコード:

public partial class MainWindow : Window
{
    // volatile keyword shall be used to avoid compiler optimizations
    // and potential multithread issues when accessing IsSomethingLoading
    // from other threads.
    private volatile bool IsSomethingLoading = false;

    public MainWindow()
    {
        InitializeComponent();

        _ = TestASyncTask();
    }

    private async Task<bool> TestASyncTask()
    {
        IsSomethingLoading = true;

        label1.Content = "Doing background task";

        // Launch the task asynchronously without waiting the end
        _ = Task.Factory.StartNew(() =>
        {
            Thread.Sleep(2000);
            IsSomethingLoading = false;
            Thread.Sleep(5000);
            HostController.Host.Invoke(new Action(() => label1.Content = "Background task terminated"));
        });
        label1.Content = "Waiting IsSomethingLoading ...";

        // Wait the flag    
        await Task.Run(async () => { while (IsSomethingLoading) { await Task.Delay(100); }});
        label1.Content = "Wait Finished";

        return true;
    }

}

/// <summary>
/// Main UI thread host controller dispatcher
/// </summary>
public static class HostController
{
    /// <summary>
    /// Main Host
    /// </summary>
    private static Dispatcher _host;
    public static Dispatcher Host
    {
        get
        {
            if (_host == null)
            {
                if (Application.Current != null)
                    _host = Application.Current.Dispatcher;
                else
                    _host = Dispatcher.CurrentDispatcher;
            }

            return _host;
        }
    }
}
于 2020-04-29T21:31:43.253 に答える