【问题标题】:Schedule and Cancel List of Tasks计划和取消任务列表
【发布时间】:2019-03-26 16:13:58
【问题描述】:

我有一个自定义小部件,它必须启动一个计划的Task 对象的列表,为简单起见,让我们以 Xamarin 的 Text-To-Speech 为例。

现在我想安排一次演讲,等待五秒钟,然后开始另一场演讲。唯一的问题是我不知道该怎么做。而且,我必须能够一次全部取消。

迭代 1:Task.ContinueWith

编辑:根据建议,我将Task.ContinueWith 与单个取消令牌一起使用:

    public CancellationTokenSource cancel_source;
    public CancellationToken cancel_token;      

    public async void Play_Clicked(object sender, System.EventArgs e)
    {
        if (!is_playing)
        {
            System.Diagnostics.Debug.Print("start");

            is_playing = true;

            cancel_source = new CancellationTokenSource();
            cancel_token = cancel_source.Token;

            current_task =
                Task.Factory.StartNew(
                    async () =>
                    {
                        System.Diagnostics.Debug.Print("first task");
                        await DependencyService.Get<ITextToSpeech>().SpeakAsync("Wait for five seconds...", cancel_source, cancel_token);
                    }
                ).ContinueWith(
                    async (arg) =>
                    {
                        System.Diagnostics.Debug.Print("wait task");
                        await Task.Delay(5000, cancel_token);
                    }
                ).ContinueWith(
                    async (arg) =>
                    {
                        System.Diagnostics.Debug.Print("last task");
                        await DependencyService.Get<ITextToSpeech>().SpeakAsync("You waited!", cancel_source, cancel_token);
                    }
                ).ContinueWith(
                    async (arg) =>
                    {
                        System.Diagnostics.Debug.Print("All done!");
                        await Task.Delay(100);
                    }
            );

            await current_task;
        }
        else
        {
            System.Diagnostics.Debug.Print("stop");

            //foreach (var p in l)   <----------------- will bother about canceling next, not right now
            //{
            //    if (p.task.IsCompleted) continue;

            //    DependencyService.Get<ITextToSpeech>().CancelSpeak();
            //    p.source.Cancel();
            //}

            is_playing = false;

            //DependencyService.Get<ITextToSpeech>().CancelSpeak();
            //cancel_source.Cancel();
            //cancel_source = null;
            //current_task = null;
        }
    }

我的实现很奇怪,当我点击按钮时,它只会说“等待 5 秒”,当我再次点击时它会说第二部分。

我的实现如下:

public class TextToSpeechImplementation : ITextToSpeech
{
    public AVSpeechSynthesizer speechSynthesizer;
    public AVSpeechUtterance speechUtterance;
    public TaskCompletionSource<bool> tcsUtterance;
    public CancellationTokenSource cancel_source;
    public CancellationToken cancel_token;

    public async Task SpeakAsync(string text, CancellationTokenSource source, CancellationToken token)
    {
        cancel_source = source;
        cancel_token = token;

        tcsUtterance = new TaskCompletionSource<bool>();

        System.Diagnostics.Debug.Print("START ASYNC IMPLEMENTATION {0}", System.DateTime.Now.ToString("HH:mm:ss"));

        var now = System.DateTime.Now;

        speechSynthesizer = new AVSpeechSynthesizer();
        speechUtterance = new AVSpeechUtterance(text);

        speechSynthesizer.DidFinishSpeechUtterance += (sender, e) => System.Diagnostics.Debug.Print("STOP ASYNC IMPLEMENTATION {0} duration {1}", System.DateTime.Now.ToString("HH:mm:ss"),
                                                                                                    (System.DateTime.Now - now).TotalSeconds);

        speechSynthesizer.DidCancelSpeechUtterance += (sender, e) => System.Diagnostics.Debug.Print("SPEECH CANCELED");

        speechSynthesizer.SpeakUtterance(speechUtterance);

        await tcsUtterance.Task;
    }

    public void CancelSpeak()
    {
        speechSynthesizer.StopSpeaking(AVSpeechBoundary.Immediate);
        tcsUtterance.TrySetResult(true);
        cancel_source.Cancel();
    }
}

我看到计划任务几乎同时运行,所以我只得到“等待 5 秒”,然后什么都没有(显然,这些任务都已完成运行)。

有什么提示吗?

迭代 2:生成任务

一直感谢 Ryan Pierce Williams,我已经修改了课程,现在唯一真正的问题是如何取消即将到来/当前任务的列表。

工作负载接口现在创建了一个取自 Xamarin 教程的 Text-To-Speech 类的新实例(我仍然想简单地玩一下!),如下所示:

public interface ITextToSpeech
{
    ITextToSpeech New(string text, CancellationTokenSource source, CancellationToken token);
    void Speak(string text);
    Task SpeakAsync(string text);
    void CancelSpeak();
}

public class TextToSpeechImplementation : ITextToSpeech
{
    public string speech_text;
    public AVSpeechSynthesizer speechSynthesizer;
    public AVSpeechUtterance speechUtterance;
    public TaskCompletionSource<bool> tcsUtterance;
    public CancellationTokenSource cancel_source;
    public CancellationToken cancel_token;

    public ITextToSpeech New(string text, CancellationTokenSource source, CancellationToken token)
    {
        speech_text = text;
        cancel_source = source;
        cancel_token = token;

        speechSynthesizer = new AVSpeechSynthesizer();
        speechUtterance = new AVSpeechUtterance(speech_text);
        speechSynthesizer.DidFinishSpeechUtterance += (sender, e) => System.Diagnostics.Debug.Print("STOP IMPLEMENTATION {0}", System.DateTime.Now.ToString("HH:mm:ss"));
        speechSynthesizer.DidCancelSpeechUtterance += (sender, e) => System.Diagnostics.Debug.Print("SPEECH CANCELED");

        return this;
    }

    public void Speak(string text)
    {
        System.Diagnostics.Debug.Print("START IMPLEMENTATION {0}", System.DateTime.Now.ToString("HH:mm:ss"));
        speechSynthesizer.SpeakUtterance(speechUtterance);
    }

    public async Task SpeakAsync(string text)
    {
        System.Diagnostics.Debug.Print("START ASYNC IMPLEMENTATION {0}", System.DateTime.Now.ToString("HH:mm:ss"));
        tcsUtterance = new TaskCompletionSource<bool>();
        speechSynthesizer.SpeakUtterance(speechUtterance);
        await tcsUtterance.Task;
    }

    public void CancelSpeak()
    {
        speechSynthesizer.StopSpeaking(AVSpeechBoundary.Immediate);
        tcsUtterance?.TrySetResult(true);
        cancel_source.Cancel();
    }
}

小部件类现在仅对工作负载使用同步调用,因为我正在生成我认为不需要async 的任务:

    public bool is_playing;
    public CancellationTokenSource cancel_source;
    public CancellationToken cancel_token;
    public List<string> l;

    public PlayerWidget(int category, int book)
    {
        is_playing = false;
        l = new List<string>();
        cancel_source = new CancellationTokenSource();
        cancel_token = cancel_source.Token;
    }

    public void Play_Clicked(object sender, System.EventArgs e)
    {
        if (!is_playing)
        {
            System.Diagnostics.Debug.Print("start");

            is_playing = true;

            l.Clear();
            l.Add("Wait for five seconds...");
            l.Add("You waited!");
            l.Add("and the last one is here for you.");
            l.Add("Just kidding, my man, you have this last sentence here and shall be perfectly said. Now I have to go... so... farewell!");

            var state = new TaskState()
            {
                Delay = 1000,
                CancellationToken = cancel_token,
                Workload = DependencyService.Get<ITextToSpeech>().New(l[0], cancel_source, cancel_token)
            };

            Task.Factory.StartNew(TaskExecutor, state, cancel_token).ContinueWith(TaskComplete);
        }
        else
        {
            // THIS DOES NOT WORK
            System.Diagnostics.Debug.Print("stop");
            is_playing = false;
            cancel_source.Cancel();
        }
    }

    public void TaskExecutor(object obj)
    {
        var state = (TaskState)obj;

        System.Diagnostics.Debug.Print("Delaying execution of Task {0} for {1} [ms] at {2}", state.TaskId, state.Delay, System.DateTime.Now.ToString("HH:mm:ss"));

        state.CancellationToken.ThrowIfCancellationRequested();

        // Delay execution, while monitoring for cancellation
        // If Task.Delay isn't responsive enough, use something like this.
        var sw = System.Diagnostics.Stopwatch.StartNew();
        while (sw.Elapsed.TotalMilliseconds < state.Delay)
        {
            Thread.Yield(); // don't hog the CPU
            state.CancellationToken.ThrowIfCancellationRequested();
        }
        System.Diagnostics.Debug.Print("Beginning to process workload of Task {0} '{1}' at {2}", state.TaskId, l[state.TaskId], System.DateTime.Now.ToString("HH:mm:ss"));

        state.Workload.Speak(l[state.TaskId]);
    }

    void TaskComplete(Task parent)
    {
        var state = (TaskState)parent.AsyncState;

        try
        {
            parent.Wait();
            System.Diagnostics.Debug.Print("Task {0} successfully completed processing its workload without error at {1}", state.TaskId, System.DateTime.Now.ToString("HH:mm:ss"));
        }
        catch (TaskCanceledException)
        {
            System.Diagnostics.Debug.Print("The Task {0} was successfully cancelled at {1}", parent.AsyncState, System.DateTime.Now.ToString("HH:mm:ss"));

            // since it was cancelled, just return. No need to continue spawning new tasks.
            return;
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.Print("An unexpected exception brought Task {0} down. {1} at {2}", state.TaskId, ex.Message, System.DateTime.Now.ToString("HH:mm:ss"));
        }

        if (state.TaskId == l.Count - 1)
        {
            is_playing = false;
        }
        else
        {
            // Kick off another task...
            var child_state = new TaskState()
            {
                Delay = 5000,
                CancellationToken = cancel_token,
                Workload = DependencyService.Get<ITextToSpeech>().New(l[state.TaskId + 1], cancel_source, cancel_token)
            };
            Task.Factory.StartNew(TaskExecutor, child_state, cancel_token).ContinueWith(TaskComplete);
        }
    }

现在它就像一个魅力,它可以正确调度,并且工作负载得到执行。不错。

现在的问题是:如何取消任务?我需要停止当前正在播放的 TTS,并阻止创建任何其他任务。我认为调用cancel_source.Cancel(); 就足够了,但事实并非如此,正如您从日志中看到的那样:

start
Delaying execution of Task 0 for 1000 [ms] at 10:21:16
Beginning to process workload of Task 0 'Wait for five seconds...' at 10:21:17
START IMPLEMENTATION 10:21:17
Task 0 successfully completed processing its workload without error at 10:21:17
Delaying execution of Task 1 for 5000 [ms] at 10:21:17
2018-10-24 10:21:17.565591+0200 TestTasks.iOS[71015:16136232] SecTaskLoadEntitlements failed error=22 cs_flags=200, pid=71015
2018-10-24 10:21:17.565896+0200 TestTasks.iOS[71015:16136232] SecTaskCopyDebugDescription: TestTasks.iOS[71015]/0#-1 LF=0
STOP IMPLEMENTATION 10:21:19
Beginning to process workload of Task 1 'You waited!' at 10:21:22
START IMPLEMENTATION 10:21:22
Task 1 successfully completed processing its workload without error at 10:21:22
Delaying execution of Task 2 for 5000 [ms] at 10:21:22
Thread started: <Thread Pool> #6
STOP IMPLEMENTATION 10:21:23
Beginning to process workload of Task 2 'and the last one is here for you.' at 10:21:27
START IMPLEMENTATION 10:21:27
Task 2 successfully completed processing its workload without error at 10:21:27
Delaying execution of Task 3 for 5000 [ms] at 10:21:27
stop
An unexpected exception brought Task 3 down. One or more errors occurred. at 10:21:27
STOP IMPLEMENTATION 10:21:29
start
An unexpected exception brought Task 4 down. One or more errors occurred. at 10:21:34
stop
start
An unexpected exception brought Task 6 down. One or more errors occurred. at 10:21:39

我的简单而幼稚的代码实际上不会立即停止当前播放的文本,它会一直持续到完成 TTS 并停止生成所有其他任务。但是,如果我再次单击播放按钮,任务将不会再次开始,并且正如您所见,我在生成新任务时遇到了奇怪的(对我而言)错误。

再次,我是新手,我该怎么办?

迭代 3:单个任务,多个令牌

像往常一样,Ryan 的建议非常有帮助,现在我已经成功编写了一个非常基本的任务处理程序,几乎可以工作:

    public void Play_Clicked(object sender, System.EventArgs e)
    {
        l.Clear();
        l.Add("Wait for five seconds...");
        l.Add("You waited!");
        l.Add("and the last one is here for you.");
        l.Add("Just kidding, my man, you have this last sentence here and shall be perfectly said. Now I have to go... so... farewell!");

        System.Diagnostics.Debug.Print("click handler playing {0}", is_playing);

        try
        {
            if (!is_playing)
            {
                System.Diagnostics.Debug.Print("start");

                cancel_source = new CancellationTokenSource();
                cancel_token = cancel_source.Token;
                current_task = new Task(SingleTask, cancel_token);
                current_task.Start();
                is_playing = true;
            }
            else
            {
                System.Diagnostics.Debug.Print("stop");

                is_playing = false;
                cancel_token.ThrowIfCancellationRequested();
                cancel_source.Cancel();
                cancel_token.ThrowIfCancellationRequested();
                current_speaker.CancelSpeak();
                cancel_token.ThrowIfCancellationRequested();
            }
        }
        catch(Exception)
        {
            System.Diagnostics.Debug.Print("cancel");

            cancel_source.Cancel();
            current_speaker.CancelSpeak();
            is_playing = false;
        }
    }

处理程序定义如下:

    public void SingleTask()
    {
        System.Diagnostics.Debug.Print("Single task started at {0}", System.DateTime.Now.ToString("HH:mm:ss"));

        foreach(var p in l)
        {
            System.Diagnostics.Debug.Print("Waiting 5s");

            //cancel_token.ThrowIfCancellationRequested();

            var sw = System.Diagnostics.Stopwatch.StartNew();
            while (sw.Elapsed.TotalMilliseconds < 5000)
            {
                Thread.Yield(); // don't hog the CPU
                //cancel_token.ThrowIfCancellationRequested();
            }

            current_speaker = DependencyService.Get<ITextToSpeech>().New(p, cancel_source, cancel_token);

            try
            { 
                System.Diagnostics.Debug.Print("Single task speaking at {0} sentence '{1}'", System.DateTime.Now.ToString("HH:mm:ss"), p);

                current_speaker.Speak(p);

                while (current_speaker.IsPlaying())
                {
                    Thread.Yield();
                }
            }
            catch (Exception)
            {
                System.Diagnostics.Debug.Print("Single task CANCELING at {0}", System.DateTime.Now.ToString("HH:mm:ss"));

                cancel_source.Cancel();
                current_speaker.CancelSpeak();
            }
        }
        System.Diagnostics.Debug.Print("Single task FINISHED at {0}", System.DateTime.Now.ToString("HH:mm:ss"));
        is_playing = false;
    }

现在任务被安排、执行并多次运行。现在的问题是取消它。

什么有效:当 TTS 说半句时终止任务。它奇怪地调用了“停止”和“取消”,但它确实有效:

click handler playing True
stop
cancel
2018-10-29 12:35:37.534358+0100[85164:17740514] [AXTTSCommon] _BeginSpeaking: couldn't begin playback
SPEECH CANCELED

什么不起作用:在等待下一个短语时终止任务。在等待期间,它再次调用“停止”和“取消”,但正如您所见,它继续执行 next 句子,然后它按我的意图停止(单击按钮)。

click handler playing False
start
Single task started at 12:36:56
Waiting 5s
Single task speaking at 12:37:01 sentence 'Wait for five seconds...'
START IMPLEMENTATION 12:37:01
STOP IMPLEMENTATION 12:37:02
Waiting 5s
Thread finished: <Thread Pool> #34
Thread started: <Thread Pool> #37
click handler playing True
stop
cancel
Single task speaking at 12:37:07 sentence 'You waited!'
START IMPLEMENTATION 12:37:07
STOP IMPLEMENTATION 12:37:08

我相信我在这里错过了一个非常小的部分!

最终解决方案

这是 Ryan 建议的最终代码,它现在可以工作了,它会在句子中间停止演讲,在等待时停止任务,这一切都是我所需要的。对于后人来说,棘手的部分是在这里混合了一个任务和本机任务(TTS 依赖服务),但现在我认为它更干净更清晰:

    public void Play_Clicked(object sender, System.EventArgs e)
    {
        l.Clear();
        l.Add("Wait for five seconds...");
        l.Add("You waited!");
        l.Add("and the last one is here for you.");
        l.Add("Just kidding, my man, you have this last sentence here and shall be perfectly said. Now I have to go... so... farewell!");

        System.Diagnostics.Debug.Print("click handler playing {0}", is_playing);

        if (!is_playing)
        {
            System.Diagnostics.Debug.Print("start");

            cancel_source = new CancellationTokenSource();
            cancel_token = cancel_source.Token;
            current_task = new Task(SingleTask, cancel_token);
            current_task.Start();
            is_playing = true;
        }
        else
        {
            System.Diagnostics.Debug.Print("stop");

            is_playing = false;
            cancel_source.Cancel();
            current_speaker.CancelSpeak();
        }
    }

    public void SingleTask()
    {
        System.Diagnostics.Debug.Print("Single task started at {0}", System.DateTime.Now.ToString("HH:mm:ss"));

        foreach(var p in l)
        {
            System.Diagnostics.Debug.Print("Waiting 5s");

            var sw = System.Diagnostics.Stopwatch.StartNew();
            while (sw.Elapsed.TotalMilliseconds < 5000)
            {
                Thread.Yield(); // don't hog the CPU

                if (cancel_source.IsCancellationRequested)
                {
                    cancel_source.Cancel();
                    current_speaker.CancelSpeak();
                    return;
                }
            }

            current_speaker = DependencyService.Get<ITextToSpeech>().New(p, cancel_source, cancel_token);

            try
            { 
                System.Diagnostics.Debug.Print("Single task speaking at {0} sentence '{1}'", System.DateTime.Now.ToString("HH:mm:ss"), p);

                current_speaker.Speak(p);

                while (current_speaker.IsPlaying())
                {
                    Thread.Yield();
                }
            }
            catch (Exception)
            {
                System.Diagnostics.Debug.Print("Single task CANCELING at {0}", System.DateTime.Now.ToString("HH:mm:ss"));

                cancel_source.Cancel();
                current_speaker.CancelSpeak();
            }
        }
        System.Diagnostics.Debug.Print("Single task FINISHED at {0}", System.DateTime.Now.ToString("HH:mm:ss"));
        is_playing = false;
    }

【问题讨论】:

  • 嘿@senseiwa,当您在取消后恢复您尝试恢复您的任务时,您将需要一个新的 CancellationTokenSource(旧的已被取消,因此使用它构建的任何未来任务都会自动取消)。
  • @RyanPierceWilliams 谢谢,我现在很接近了!我已根据您的建议编辑了代码,现在它几乎可以工作了。它会产生一个任务,说话,并取消中间的句子,如果我再次点击,它会重新启动。唯一不起作用的部分是它等待下一句时。像往常一样,非常欢迎任何提示,您让我了解一卡车的任务/线程!谢谢!
  • 很高兴它有帮助 :) 至于您剩下的问题,我已经查看了您更新的代码,并且立即弹出给我的是您正在对取消令牌执行检查错误的位置。这些检查应该在任务线程中进行,而不是在您的 UI 线程中。 UI 线程应该告诉 CancellationTokenSource 实例取消,而任务应该检查从 CancellationTokenSource 检索到的提供的 CancellationToken 以查看是否已请求取消。
  • 另外,如果您不希望在任务线程中引发异常,您可以随时使用 IsCancellationRequested 属性,它只会给您一个布尔值。
  • 它有效@RyanPierceWilliams,谢谢!我已经编辑了这个问题,标记为已解决,如果你在我的代码中看到一些可疑的东西,请毫不犹豫地告诉我。感谢您的大力帮助,混合任务、本机任务和处理所有启动/停止/取消事件并不容易。谢谢!

标签: c# .net xamarin xamarin.forms


【解决方案1】:

在 .NET 的任务库中,取消被视为异步任务必须响应的请求(例外:如果框架检测到已请求取消,则尚未开始运行的计划任务可能会被框架取消)。

为了让任务检查是否已请求取消,您必须将 CancellationToken 传递给任务。这可以作为(或作为)可选状态参数的一部分来完成。下面是一个任务示例,该任务将处于无限循环中,直到请求取消:

Sub Main()
    Dim cts As New CancellationTokenSource()
    Dim ct = cts.Token
    Dim t = Task.Factory.StartNew(AddressOf InfiniteLoop, ct, ct)

    Thread.Sleep(5000)
    Console.WriteLine("Task Status after 5000 [ms]: {0}", t.Status)
    Debug.Assert(t.Status = TaskStatus.Running)

    cts.Cancel()
    Try
        t.Wait()
    Catch ex As Exception
        Console.WriteLine("ERROR: {0}", ex.Message)
    End Try

    Console.WriteLine("Task Status after cancelling: {0}", t.Status)
    Console.WriteLine("Press enter to exit....")
    Console.ReadLine()
End Sub

Public Sub InfiniteLoop(ByVal ct As CancellationToken)
    While True
        ct.ThrowIfCancellationRequested()
    End While
End Sub

至于同步做任务,只需要维护一个工作队列(ConcurrentQueue)即可​​。对您运行的每个任务使用 Task.ContinueWith(...),以便它可以启动队列中的下一个项目(或全部取消)。

您可以使用 Task.Delay(5000) 启动需要 5 秒才能完成的任务。使用 Task.Delay(5000).ContinueWith( myTask ) 延迟任务的执行。

编辑:你描述它的方式,听起来你只是想不断地产生新的任务,直到有人告诉你停止。我在下面编写了一个示例应用程序,可以做到这一点:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace TaskQueueExample
{
class Program
{
    public class TaskState
    {
        private static int _taskCounter = 0;

        public int TaskId { get; set;  }
        public int Delay { get; set; }
        public int Workload { get; set; }
        public CancellationToken CancellationToken { get; set; }

        public TaskState()
        {
            TaskId = _taskCounter;
            _taskCounter++;
        }
    }

    static CancellationTokenSource _cts = new CancellationTokenSource();
    static Random _rand = new Random();

    static void Main(string[] args)
    {
        var state = new TaskState() { Delay = _rand.Next(0, 1000), Workload= _rand.Next(0, 1000), CancellationToken = _cts.Token };

        Task.Factory.StartNew(Program.DoSomeWork, state, _cts.Token).ContinueWith(Program.OnWorkComplete);

        Console.WriteLine("Tasks will start running in the background. Press enter at any time to exit.");
        Console.ReadLine();

        _cts.Cancel();
    }

    static void DoSomeWork(object obj)
    {
        if (obj == null) throw new ArgumentNullException("obj");
        var state = (TaskState)obj;

        Console.WriteLine("Delaying execution of Task {0} for {1} [ms]", state.TaskId, state.Delay);

        state.CancellationToken.ThrowIfCancellationRequested();

        // Delay execution, while monitoring for cancellation
        // If Task.Delay isn't responsive enough, use something like this.
        var sw = Stopwatch.StartNew();
        while(sw.Elapsed.TotalMilliseconds < state.Delay)
        {
            Thread.Yield(); // don't hog the CPU
            state.CancellationToken.ThrowIfCancellationRequested();
        }

        Console.WriteLine("Beginning to process workload of Task {0}", state.TaskId);

        // Simulate a workload (NOTE: no Thread.Yield())
        sw.Restart();
        while(sw.Elapsed.TotalMilliseconds < state.Workload)
        {                
            state.CancellationToken.ThrowIfCancellationRequested();
        }           
    }

    static void OnWorkComplete(Task parent)
    {
        var state = (TaskState)parent.AsyncState;

        try
        {
            parent.Wait();
            Console.WriteLine("Task {0} successfully completed processing it's workload without error.", state.TaskId);
        }
        catch(TaskCanceledException)
        {
            Console.WriteLine("The Task {0} was successfully cancelled.", parent.AsyncState);

            // since it was cancelled, just return. No need to continue spawning new tasks.
            return;
        }
        catch(Exception ex)
        {
            Console.WriteLine("An unexpected exception brought Task {0} down. {1}", state.TaskId, ex.Message);
        }

        // Kick off another task...
        var child_state = new TaskState() { Delay = _rand.Next(0, 1000), Workload = _rand.Next(0, 1000), CancellationToken = _cts.Token };
        Task.Factory.StartNew(Program.DoSomeWork, child_state, _cts.Token).ContinueWith(Program.OnWorkComplete);
    }


}
}

【讨论】:

  • 谢谢,Ryan,任务列表 怎么样,我可以处理一个任务,但是使用计时器处理多个计划任务的正确方法现在有点混乱对我来说。
  • 任务列表的处理方式相同。启动时将取消令牌传递给每个任务(全部来自同一个 CancellationTokenSource)。然后每个任务必须定期评估其取消令牌,以便它可以处理取消请求。
  • 不要使用定时器。如上所述,使用 ConcurrentQueue 来跟踪计划任务。使用 Task.ContinueWith(...) 在当前任务完成后安排下一个任务。
  • @senseiwa - 我在我的帖子中添加了一个示例应用程序,我认为这可能是您正在寻找的。我假设您想要生成恒定的任务流,而不仅仅是处理固定的任务列表。无论哪种方式,它的代码都是一样的——如果你只有一个特定的有限列表要做,只需从任务队列中弹出,而不是生成一个新任务。
  • @senseiwa - 有不同的方法可以解决这个问题。您可以使用一个通用的 TaskState 来跟踪您在任务序列中的位置,而不是每次启动任务时都生成一个新的 TaskState。只要你持有这个TaskState,你就可以根据TaskState中的数据从你离开的地方重新开始。希望这会有所帮助:)
猜你喜欢
  • 1970-01-01
  • 2012-07-08
  • 2020-11-18
  • 2011-05-01
  • 2012-04-19
  • 2010-12-21
  • 2014-05-22
  • 2021-03-28
  • 2011-07-26
相关资源
最近更新 更多