简体   繁体   中英

Is the order of resume multiple await expressions to the same task is guaranteed?

I'm learning about async/await with C# 4.5 and wondering what happens when multiple "awaiters" are awaiting the same task.

I'm doing some experimentation and I think that I understand what's happening. However, i have some questions to ask:

Look at this piece of code:

AsyncAwaitExperiment.fxml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace View
{
    /// <summary>
    /// Interaction logic for AsyncAwaitExperiment.xaml
    /// </summary>
    public partial class AsyncAwaitExperiment : Window
    {

        private CancellationTokenSource token;
        private Task longRunningTask;

        public AsyncAwaitExperiment()
        {
            InitializeComponent();
        }

        private void startAsyncOperation(object sender, RoutedEventArgs e)
        {
            if (longRunningTask != null && !longRunningTask.IsCompleted)
            {
                return;
            }
            else
            {
                longRunningTask = null;
                token = new CancellationTokenSource();
            }

            Action action = () => 
            {
                while (!token.Token.IsCancellationRequested)
                {
                    Thread.Sleep(100);
                }
            };

            longRunningTask = Task.Factory.StartNew(
                action,
                token.Token, 
                TaskCreationOptions.LongRunning,
                TaskScheduler.Current);
        }

        private void stopOperation(object sender, RoutedEventArgs e)
        {
            if (longRunningTask == null
                || longRunningTask.IsCompleted
                || token == null
                || token.Token.IsCancellationRequested)
            {
                return;
            }
            token.Cancel();
        }

        private async void firstAwait(object sender, RoutedEventArgs e)
        {
            if (longRunningTask == null || longRunningTask.IsCompleted)
                return;
            await longRunningTask;
            Console.WriteLine("First Method");
        }

        private async void secondAwait(object sender, RoutedEventArgs e)
        {
            if (longRunningTask == null || longRunningTask.IsCompleted)
                return;
            await longRunningTask;
            Console.WriteLine("Second Method");
        }
    }
}

AsyncAwaitExperiment.fxml

<Window x:Class="View.AsyncAwaitExperiment"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="AsyncAwaitExperiment" Height="300" Width="300">
    <Grid>
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
            <Button Content="Start Async" Margin="3" Click="startAsyncOperation" />
            <Button Content="Stop Async" Margin="3" Click="stopOperation" />
            <Button Content="Await First" Margin="3" Click="firstAwait" />
            <Button Content="Await Second" Margin="3" Click="secondAwait" />
        </StackPanel>
    </Grid>
</Window>

The behavior of this code is:

  1. Start an async long running operation when the button "start" is pressed.
  2. Stop the async operation when the button "stop" is pressed.
  3. When the "first Await" button is pressed, await for the task to finish and then print something on the console.
  4. The same with the second button ("second await").

With this piece of code and some break points I have noticed some behaviors.

  1. When i await a task it will resume the code at the point that it was stopped previously. Nothing new here, i guess.
  2. If (this is the interesting part) I have more than one "awaiter" for the same task, it will resume in same order of the code that awaited. EX: if the firstAwait was clicked first and the secondAwait second, when the task finishes and the resume of the code flow will follow the same order: first await and then second await.

The questions are:

  1. Is there any guarantee of this behavior (about the order of the resume) ?
  2. Is there any problem in using this scheduler (TaskScheduler.Current) ?
  3. Can I re-use the cancellation token or do I really need to create one every time that I cancel and start a task ?

PS: Sorry for my bad english :/

You shouldn't assume anything about the order in which activities will resume. Supposing that, in the future, someone managed to write a new UI system that was entirely multi-threaded, you'd expect even UI threads to run in parallel (Notes: No-one thinks it's sane to write a multi-threaded UI system currently; Even if two tasks resume "at the same time", one of them could be indefinitely paused)

If there are dependencies between activities in your code, make them explicit. Otherwise, assume that they may occur in any order and may be interleaved. That's the only sane way to program.

And, finally, no. Once you've used a CancellationToken , it's done. If you need another one, you need to create a new one. This actually makes thinking about multiple tasks/activities simpler to think about - no token can go from being cancelled to being uncancelled.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM