Async Tasks Pause Resume of Loop [duplicate]

2019-05-28 13:50发布

问题:

This question already has an answer here:

  • A pattern to pause/resume an async task? 3 answers

I have a sample code, taken from MSDN, to which I would like implement the Pause / Resume functionality within while loop. Could anybody propose a solution / parern which would hande this?

 private async void startButton_Click(object sender, RoutedEventArgs e)
    {
        resultsTextBox.Clear();

        // Instantiate the CancellationTokenSource.
        cts = new CancellationTokenSource();

        try
        {
            await AccessTheWebAsync(cts.Token);
            resultsTextBox.Text += "\r\nDownloads complete.";
        }
        catch (OperationCanceledException)
        {
            resultsTextBox.Text += "\r\nDownloads canceled.\r\n";
        }
        catch (Exception)
        {
            resultsTextBox.Text += "\r\nDownloads failed.\r\n";
        }

        cts = null;
    }


    private void cancelButton_Click(object sender, RoutedEventArgs e)
    {
        if (cts != null)
        {
            cts.Cancel();
        }
    }


    async Task AccessTheWebAsync(CancellationToken ct)
    {
        HttpClient client = new HttpClient();

        // Make a list of web addresses.
        List<string> urlList = SetUpURLList();

        // ***Create a query that, when executed, returns a collection of tasks.
        IEnumerable<Task<int>> downloadTasksQuery =
            from url in urlList select ProcessURL(url, client, ct);

        // ***Use ToList to execute the query and start the tasks. 
        List<Task<int>> downloadTasks = downloadTasksQuery.ToList();

        // ***Add a loop to process the tasks one at a time until none remain. 
        while (downloadTasks.Count > 0)
        {
                // Identify the first task that completes.
                Task<int> firstFinishedTask = await Task.WhenAny(downloadTasks);

                // ***Remove the selected task from the list so that you don't 
                // process it more than once.
                downloadTasks.Remove(firstFinishedTask);

                // Await the completed task. 
                int length = await firstFinishedTask;
                resultsTextBox.Text += String.Format("\r\nLength of the download:  {0}", length);
        }
    }

回答1:

There is an MSDN article that solves this using a PauseToken (similar to a CancellationToken).

Here's the sample code from that article that demonstrates this concept:

namespace PauseTokenTestApp
{
    public class PauseTokenSource
    {
        private volatile TaskCompletionSource<bool> m_paused;
        internal static readonly Task s_completedTask = Task.FromResult(true);

        public bool IsPaused
        {
            get { return m_paused != null; }
            set
            {
                if (value)
                {
                    Interlocked.CompareExchange(
                        ref m_paused, new TaskCompletionSource<bool>(), null);
                }
                else
                {
                    while (true)
                    {
                        var tcs = m_paused;
                        if (tcs == null) return;
                        if (Interlocked.CompareExchange(ref m_paused, null, tcs) == tcs)
                        {
                            tcs.SetResult(true);
                            break;
                        }
                    }
                }
            }
        }
        public PauseToken Token { get { return new PauseToken(this); } }

        internal Task WaitWhilePausedAsync()
        {
            var cur = m_paused;
            return cur != null ? cur.Task : s_completedTask;
        }
    }

    public struct PauseToken
    {
        private readonly PauseTokenSource m_source;
        internal PauseToken(PauseTokenSource source) { m_source = source; }

        public bool IsPaused { get { return m_source != null && m_source.IsPaused; } }

        public Task WaitWhilePausedAsync()
        {
            return IsPaused ?
                m_source.WaitWhilePausedAsync() :
                PauseTokenSource.s_completedTask;
        }
    }

    class Program
    {
        static void Main()
        {
            var pts = new PauseTokenSource();
            Task.Run(() =>
            {
                while (true)
                {
                    Console.ReadLine();
                    pts.IsPaused = !pts.IsPaused;
                }
            });
            SomeMethodAsync(pts.Token).Wait();
        }

        public static async Task SomeMethodAsync(PauseToken pause)
        {
            for (int i = 0; i < 100; i++)
            {
                Console.WriteLine(i);
                await Task.Delay(100);
                await pause.WaitWhilePausedAsync();
            }
        }
    }
}