Остановка и запуск задач с использованием CancellationTokenSource

#c# #multithreading #scheduled-tasks #cancellationtokensource

#c# #многопоточность #запланированные задачи #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:tempMy_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. Отмена!= приостановка. Вы не можете перезапустить отмененную задачу. Используйте что-то помимо токена отмены, чтобы приостановить выполнение.

2. Чего вы пытаетесь достичь этим?

3. Как я могу полностью остановить / очистить задачу и запустить ее снова с новыми параметрами? Я не показал здесь полный код для простоты. Просто хочу понять, как повторно использовать тот же код задачи снова как новый.

4. Добавьте tokenSource = new CancellationTokenSource(); внутрь PrintButtonText перед Task.Factory... .

5. Спасибо, что работает! Но означает ли это, что старая задача тоже остановлена и не использует память в фоновом режиме?

Ответ №1:

Вы должны использовать Microsoft Reactive Framework (он же Rx) — NuGet System.Reactive.Windows.Forms и add 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() , чтобы закрыть все это.

Это намного проще, чем возиться с токенами отмены.

Комментарии:

1. Привет, я весьма заинтригован этой реактивной функциональностью (я не просматривал ее раньше). Какие-либо рекомендуемые руководства или ресурсы?