我想编写一个方法,await
将变量设置为true。
这是psudo代码。
bool IsSomethingLoading = false
SomeData TheData;
public async Task<SomeData> GetTheData()
{
await IsSomethingLoading == true;
return TheData;
}
TheData
将由Prism事件和IsSomethingLoading
变量设置。
我调用了GetTheData
方法,但是我想让它运行异步(现在它只是在数据没有准备好的情况下返回null。(这会导致其他问题。)
有办法做到这一点吗?
答案 0 :(得分:14)
在许多情况下,您需要的是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
。
答案 1 :(得分:9)
您可以使用TaskCompletionSource作为信号,await
表示:
TaskCompletionSource<bool> IsSomethingLoading = new TaskCompletionSource<bool>();
SomeData TheData;
public async Task<SomeData> GetTheData()
{
await IsSomethingLoading.Task;
return TheData;
}
在你的Prism事件中:
IsSomethingLoading.SetResult(true);
答案 2 :(得分:1)
如果您不关注快速性能,我建议一个非常简单的解决方案,但不是最好的答案:
...
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;
}
}
}
答案 3 :(得分:-1)
这对我有用:
while (IsLoading) await Task.Delay(100);