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?";
    }