#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. Привет, я весьма заинтригован этой реактивной функциональностью (я не просматривал ее раньше). Какие-либо рекомендуемые руководства или ресурсы?