C# 为什么Task.WaitAll()在这里不阻塞或导致死锁?
在下面的示例中,使用了两个C# 为什么Task.WaitAll()在这里不阻塞或导致死锁?,c#,xamarin,task-parallel-library,async-await,C#,Xamarin,Task Parallel Library,Async Await,在下面的示例中,使用了两个wait调用。为了提高性能,示例将被转换为Task.WaitAll()(实际上没有更快,但这只是一个示例) 这是Android上使用Sqlite.Net的库中的代码,该方法从主UI线程上的OnResume()调用: public async Task SetupDatabaseAsync() { await CreateTableAsync<Session>(); await CreateTableAsync<Speaker>(); }
wait
调用。为了提高性能,示例将被转换为Task.WaitAll()
(实际上没有更快,但这只是一个示例)
这是Android上使用Sqlite.Net的库中的代码,该方法从主UI线程上的OnResume()
调用:
public async Task SetupDatabaseAsync()
{
await CreateTableAsync<Session>();
await CreateTableAsync<Speaker>();
}
公共异步任务SetupDatabaseAsync()
{
等待CreateTableAsync();
等待CreateTableAsync();
}
以下是备选方案:
public void SetupDatabaseAsync()
{
var t1 = CreateTableAsync<Session>();
var t2 = CreateTableAsync<Speaker>();
Task.WaitAll(t1, t2);
}
public void SetupDatabaseAsync()
{
var t1=CreateTableAsync();
var t2=CreateTableAsync();
Task.WaitAll(t1,t2);
}
但是根据我的理解,Task.WaitAll()
应该在等待时阻塞UI线程,从而导致死锁。但它工作得很好。这是因为这两个调用实际上没有调用UI线程上的任何东西吗
如果改用
Task.WhenAll()
有什么区别?我猜想,即使调用UI线程,它也会起作用,就像阻塞UI线程(和当前同步上下文)时使用的wait
一样。只有在等待的任务之一封送委托到当前上下文,然后等待时,才会导致死锁(同步或异步)。任何异步方法上的同步阻塞在每种情况下都不是即时死锁
由于默认情况下,async
方法将把方法的其余部分封送到当前同步上下文中,并且在每次await
之后,由于任务将永远不会完成,这意味着同步等待使用async/await
的方法通常会死锁;至少不会死锁s所描述的行为被显式覆盖(例如通过ConfigureAwait(false)
)
使用WhenAll
意味着您没有阻塞当前同步上下文。您只是计划在所有其他任务完成时运行另一个继续,而不是阻塞线程,让上下文自由地处理当前准备好的任何其他请求(比如说,从底层的async
方法的延续,当所有的都在等待时)。我在我的博客上描述了。我还有一个例子,你可能会觉得有用
总之,Task.WaitAll
将在您的场景中死锁,但仅当任务需要同步回UI线程才能完成时。您可以得出结论,CreateTableAsync()
不会同步回UI线程
相反,此代码将死锁:
public async Task SetupDatabaseAsync()
{
await CreateTableAsync<Session>();
await CreateTableAsync<Speaker>();
}
Task.WaitAll(SetupDatabaseAsync());
“一路异步”是我在我的中推荐的准则之一。也许这个示例将演示可能发生的情况。这是一个iOS视图加载。使用wait调用和不使用wait调用都可以尝试它(下面注释掉)。如果函数中没有任何wait,它将同步运行,UI将被阻止
public async override void ViewDidLoad()
{
base.ViewDidLoad ();
var d1 = Task.Delay (10);
var d2 = Task.Delay (10000);
//await Task.Delay (10);
Task.WaitAll (d1, d2);
this.label.Text = "Tasks have ended - really!";
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear (animated);
this.label.Text = "Tasks have ended - or have they?";
}
你的例子不一样,第一个任务在第一个任务完成后才开始第二个任务。为什么你认为阻塞UI线程会导致死锁?@Lee正是如此。这就是为什么我写这个来优化并让它们并行执行的原因,方法改为使用task.WaitAll()@HamletHakobyan好吧,如果使用等待
,这真的很容易。你只需要同步等待使用等待
的任务。要发生死锁,必须满足一些条件:-互斥-资源持有-循环等待和-无抢占。如果其中一个丢失,你就不会出现死锁。你是不过,示例并没有给出太多信息。你说whalll
没有阻塞。但是为什么WaitAll
也没有阻塞呢?我觉得我需要更好地了解同步上下文。有什么阅读建议吗?@Krumelur从MSDN上的类文档开始,就像你对任何你感兴趣的类一样布特。@Krumelur为什么WaitAll
block?因为它被编码为block。它的生活目的是阻止任务完成。whalll
只是创建一个新任务,几乎不需要任何时间,然后基本上立即返回,让执行继续以其愉快的方式进行……不管它是什么代码,这些任务都会执行ys.@Krumelur,正如所建议的那样,MSDN是一个好的开始。你也可以浏览Stephen Toub博客。问题不仅仅是阻止UI,而是死锁。哦,顺便说一句,这段代码在两种情况下都会阻止UI,而不仅仅是一种情况。
public async override void ViewDidLoad()
{
base.ViewDidLoad ();
var d1 = Task.Delay (10);
var d2 = Task.Delay (10000);
//await Task.Delay (10);
Task.WaitAll (d1, d2);
this.label.Text = "Tasks have ended - really!";
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear (animated);
this.label.Text = "Tasks have ended - or have they?";
}