.net 写一个异步方法等待一个bool

t9eec4r0  于 2023-05-19  发布在  .NET
关注(0)|答案(5)|浏览(131)

我想写一个方法,将await的变量设置为true。
下面是psudo代码。

bool IsSomethingLoading = false
SomeData TheData;

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

TheData将由棱镜事件沿着IsSomethingLoading变量一起设置。
我调用了GetTheData方法,但我希望它运行async(现在如果数据没有准备好,它只返回null。(这会导致其他问题。)
有办法做到这一点吗?

7vux5j2d

7vux5j2d1#

在许多情况下,您需要的是TaskCompletionSource
您可能有一个方法能够在某个时间点生成数据,但它不使用任务来完成。也许有一个方法接受一个提供结果的回调函数,或者有一个事件被触发来指示有一个结果,或者只是使用一个ThreadThreadPool的代码,而你不想用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提供给worker或其他类,或者以其他方式将其公开到更广泛的范围,但我发现通常不需要它,即使在适当的时候它是一个非常强大的选项。
也可以使用Task.FromAsync创建基于异步操作的任务,然后直接返回该任务,或者在代码中使用await返回该任务。

t0ybt7op

t0ybt7op2#

您可以使用TaskCompletionSource作为信号,而await

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

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

在您的Prism活动中,请执行以下操作:

IsSomethingLoading.SetResult(true);
qvk1mo1f

qvk1mo1f3#

这项工作对我来说:

while (IsLoading) await Task.Delay(100);
lbsnaicq

lbsnaicq4#

bool IsSomethingLoading = false
SomeData TheData;

public async Task<SomeData> GetTheData()
{
   while(IsSomethingLoading != true)
      await Task.Yield();

   return TheData;
}

最简单的解决方案

afdcj2ne

afdcj2ne5#

我提出了一个非常简单的解决方案,但不是最好的回答原始问题,如果你不考虑速度性能:

...
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;
        }
    }
}

相关问题