使用CancellationTokenSource停止和启动任务

时间:2019-03-20 18:13:54

标签: c# multithreading scheduled-tasks cancellationtokensource

这只是我正在尝试做的简化版本

public partial class MainWindow : Window
{
    private CancellationTokenSource tokenSource = new CancellationTokenSource();

    public MainWindow()
    {
        InitializeComponent();
        PrintButtonText("None");
    }

    private void PrintButtonText(string buttonText)
    {
        Console.WriteLine("Update!");
        Task.Factory.StartNew(() =>
        {
            while (!tokenSource.Token.IsCancellationRequested)
            {
                Console.WriteLine("Button Pressed Text: " + buttonText);
            }
        }, tokenSource.Token);
    }

    private void Button1_Click(object sender, RoutedEventArgs e)
    {
        tokenSource.Cancel();
        PrintButtonText("Button1");
    }

    private void Button2_Click(object sender, RoutedEventArgs e)
    {
        tokenSource.Cancel();
        PrintButtonText("Button2");
    }
}

我做完

tokenSource.Cancel();
PrintButtonText("Button1");

它无法再次启动任务并继续打印我的行。我需要它以这种方式为我的程序工作。

我想停止线程并使用一些不同的参数重新启动它。我该如何实现?谢谢

编辑

由于我没有获得简化版本的解决方案,因此这里有完整的代码以及我想做的事情。基本上,在wpf窗口上,摄影机渲染从启动开始。有一个按钮可以开始保存到文件,但是要保存,我必须更新配置并再次启动“管道”。

public partial class MainWindow : Window
{
    private Pipeline pipeline = new Pipeline(); // Create and config the pipeline to sream color and depth frames.
    private CancellationTokenSource tokenSource = new CancellationTokenSource();

    private bool saveDataToFile = false;

    public MainWindow()
    {
        InitializeComponent();
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        Config cfg = SetupConfig(false);
        PipelineProfile pp = pipeline.Start(cfg);
        StartRenderFrames(pp);
    }

    private void StartRenderFrames(PipelineProfile pp)
    {
        Colorizer colorizer = new Colorizer(); // The colorizer processing block used to visualize the depth frames.

        // Allocate bitmaps for rendring. Since the sample aligns the depth frames to the color frames, both of the images will have the color resolution
        using (var p = pp.GetStream(Stream.Color) as VideoStreamProfile)
        {
            imgColor.Source = new WriteableBitmap(p.Width, p.Height, 96d, 96d, PixelFormats.Rgb24, null);
            imgDepth.Source = new WriteableBitmap(p.Width, p.Height, 96d, 96d, PixelFormats.Rgb24, null);
        }
        Action<VideoFrame> updateColor = UpdateImage(imgColor);
        Action<VideoFrame> updateDepth = UpdateImage(imgDepth);

        Task.Factory.StartNew(() =>
        {
            while (!tokenSource.Token.IsCancellationRequested)
            {
                // Wait for the next available FrameSet
                using (var frames = pipeline.WaitForFrames())
                {
                    var colorFrame = frames.ColorFrame.DisposeWith(frames);
                    var depthFrame = frames.DepthFrame.DisposeWith(frames);

                    // We colorize the depth frame for visualization purposes, .
                    var colorizedDepth = colorizer.Process(depthFrame).DisposeWith(frames);

                    // Render the frames.
                    Dispatcher.Invoke(DispatcherPriority.Render, updateDepth, colorizedDepth);
                    Dispatcher.Invoke(DispatcherPriority.Render, updateColor, colorFrame);
                }
            }
        }, tokenSource.Token);
    }

    private Config SetupConfig(bool saveDepthFile)
    {
        Config cfg = new Config();
        cfg.EnableStream(Stream.Depth, 640, 480, framerate: 15);
        cfg.EnableStream(Stream.Color, 640, 480, format: Format.Rgb8, framerate: 15);
        if (saveDepthFile)
        {
            cfg.EnableRecordToFile(@"C:\temp\My_test111.bag");
        }
        return cfg;
    }

    static Action<VideoFrame> UpdateImage(Image img)
    {
        var wbmp = img.Source as WriteableBitmap;
        return new Action<VideoFrame>(frame =>
        {
            using (frame)
            {
                var rect = new Int32Rect(0, 0, frame.Width, frame.Height);
                wbmp.WritePixels(rect, frame.Data, frame.Stride * frame.Height, frame.Stride);
            }
        });
    }

    private void StartSaving_Button_Click(object sender, RoutedEventArgs e)
    {
        tokenSource.Cancel();
        pipeline.Stop();
        // This is where I have a problem. Rendering thread does not stop before I want to start again.
        Config cfg = SetupConfig(true);
        PipelineProfile pp = pipeline.Start(cfg);
        StartRenderFrames(pp);
    }
}

1 个答案:

答案 0 :(得分:0)

您应该使用Microsoft的Reactive Framework(又名Rx)-NuGet System.Reactive.Windows.Forms并添加using System.Reactive.Linq;-然后您可以执行以下操作:

    private void Form1_Load(object sender, EventArgs e)
    {
        IObservable<string> button1Clicks =
            Observable
                .FromEventPattern<EventHandler, EventArgs>(h => button1.Click += h, h => button1.Click -= h)
                .Select(ep => "Button1");

        IObservable<string> button2Clicks =
            Observable
                .FromEventPattern<EventHandler, EventArgs>(h => button2.Click += h, h => button2.Click -= h)
                .Select(ep => "Button2");

        IDisposable subscription =
            button1Clicks
                .Merge(button2Clicks)
                .StartWith("None")
                .Select(x => Observable.Timer(TimeSpan.Zero, TimeSpan.FromMilliseconds(500.0)).Select(n => x))
                .Switch()
                .ObserveOn(this)
                .Subscribe(x => Console.WriteLine(x));
    }

这就是完成您想要的工作所需的全部代码。

您唯一需要做的就是将subscription移到private字段中,然后只需调用subscription.Dispose()将其关闭。

这比弄乱取消令牌要简单得多。