簡體   English   中英

C#Task.WaitAll()如何將對象狀態合並為一個?

[英]How does C# Task.WaitAll() combine object states into one?

以一個簡單的酒店實體為例:

class Hotel
{
    public int NumberOfRooms { get; set; }
    public int StarRating { get; set; }
}

請考慮C#5.0中的以下代碼:

public void Run()
{
    var hotel = new Hotel();
    var tasks = new List<Task> { SetRooms(hotel), SetStars(hotel) };
    Task.WaitAll(tasks.ToArray());
    Debug.Assert(hotel.NumberOfRooms.Equals(200));
    Debug.Assert(hotel.StarRating.Equals(5));
}

public async Task SetRooms(Hotel hotel)
{
    await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
    hotel.NumberOfRooms = 200;
}

public async Task SetStars(Hotel hotel)
{
    await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
    hotel.StarRating = 5;
}

兩次對Debug.Assert()的調用都成功通過。 我不明白在兩個任務完成后,Hotel的實例包含兩個並行運行的方法的賦值。

我認為當調用await時(在SetRooms()SetStars() ),會創建一個酒店實例的“快照”(將NumberOfRoomsStarRating設置為0)。 所以我的期望是兩個任務之間會有競爭條件,最后一個將被復制回hotel ,在兩個屬性中的一個產生0。

顯然我錯了。 你能解釋我在哪里誤解了等待是如何工作的嗎?

我認為當調用await時(在SetRooms()和SetStars()中),會創建一個酒店實例的“快照”

您的Hotel類是參考類型。 當您使用async-await時,您的方法將轉換為狀態機,並且該狀態機將對您的變量的引用提升到它上面。 這意味着創建的兩個狀態機都指向同一個Hotel實例 您的Hotel沒有“快照”或深層副本,編譯器不這樣做。

如果要查看實際情況, 可以查看編譯器在轉換異步方法后發出的內容:

[AsyncStateMachine(typeof(C.<SetRooms>d__1))]
public Task SetRooms(Hotel hotel)
{
    C.<SetRooms>d__1 <SetRooms>d__;
    <SetRooms>d__.hotel = hotel;
    <SetRooms>d__.<>t__builder = AsyncTaskMethodBuilder.Create();
    <SetRooms>d__.<>1__state = -1;
    AsyncTaskMethodBuilder <>t__builder = <SetRooms>d__.<>t__builder;
    <>t__builder.Start<C.<SetRooms>d__1>(ref <SetRooms>d__);
    return <SetRooms>d__.<>t__builder.Task;
}
[AsyncStateMachine(typeof(C.<SetStars>d__2))]
public Task SetStars(Hotel hotel)
{
    C.<SetStars>d__2 <SetStars>d__;
    <SetStars>d__.hotel = hotel;
    <SetStars>d__.<>t__builder = AsyncTaskMethodBuilder.Create();
    <SetStars>d__.<>1__state = -1;
    AsyncTaskMethodBuilder <>t__builder = <SetStars>d__.<>t__builder;
    <>t__builder.Start<C.<SetStars>d__2>(ref <SetStars>d__);
    return <SetStars>d__.<>t__builder.Task;
}

您可以看到兩種方法都將hotel變量提升到狀態機中。

所以我的期望是兩個任務之間會有競爭條件,最后一個將被復制回酒店,在兩個屬性中的一個產生0。

既然您已經看到編譯器實際執行的操作,您就可以理解確實沒有競爭條件。 這是正在修改的Hotel的相同實例,每個方法設置不同的變量。


邊注

也許您編寫此代碼只是為了解釋您的問題,但如果您已經在創建異步方法,我建議使用Task.WhenAll而不是阻塞Task.WaitAll 這意味着將Run的簽名更改為async Task而不是void

public async Task RunAsync()
{
    var hotel = new Hotel();
    await Task.WhenAll(SetRooms(hotel), SetStars(hotel));
    Debug.Assert(hotel.NumberOfRooms.Equals(200));
    Debug.Assert(hotel.StarRating.Equals(5));
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM