@NikolayKhablenko

Как мгновенно завершить Task?

Всем привет. Может кто то откликнется на мой вопрос. В общем разрабатываю приложение, WPF, в окне есть три элемента IMAGE. Есть флаг анимаций. Если флаг выключен - то в Image вывожу обычные файлы PNG.
Так же на картинки есть анимации, но не на все. Например на 1 и 3 есть анимация а на 2 нету.
Если флаг включен, то я вывожу обратно все PNG, а после, с помощью Task(поскольку wpf не поддерживает GIF, вывожу в отдельном потоке через перерисовку каждого фрейма), запускаю анимацию. Вся соль заключается в том, что, иногда поток остается, и при выборе других картинок и анимаций, в элементе подвисает фрейм предыдущей анимации.
Вопрос - как можно полностью, и желательно, мгновенно, завершить TASKи чтобы он не висел в памяти

Вот код самого Вывода
static CancellationTokenSource cancelTokenSource3 = new CancellationTokenSource();
        CancellationToken token3 = cancelTokenSource3.Token;
        public Task th_Image3;

        volatile bool volatileComplette1 = false;
        volatile bool volatileComplette2 = false;
        volatile bool volatileComplette3 = false;

                GifBitmapDecoder decoder3;
                BitmapSource bitmapSource3;
                int frameCount3;


                            th_Image3 = new Task(() =>
                            {
                                while (!volatileComplette3)
                                {
                                   
                                    if (token3.IsCancellationRequested)
                                    {
                                        return;
                                    }
                                    for (int c = 0; ((c < frameCount3)&& (!volatileComplette3)); c++)
                                    {
                                       
                                        this.Dispatcher.Invoke(new Action(delegate ()
                                        {
                                            bitmapSource3 = decoder3.Frames[c];
                                            Image_Right.Source = bitmapSource3;
                                            
                                            if (token3.IsCancellationRequested)
                                            {
                                                return;
                                            }
                                        }));
                                        System.Threading.Thread.Sleep(30);
                                        if (token3.IsCancellationRequested)
                                        {
                                            return;
                                        }

                                    }
                                
                              
                                    if (token3.IsCancellationRequested)
                                    {
                                        return;
                                    }
                                }
                         
                            }, token3);
                            th_Image3.Start();
  • Вопрос задан
  • 205 просмотров
Решения вопроса 1
gdt
@gdt
Программист
Извините, что не отвечаю на ваш вопрос - а почему не используете https://github.com/XamlAnimatedGif/XamlAnimatedGif ?

По делу - если мне не изменяет память, нет простого способа прибить Task, для этого нужно использовать CancellationTokenSource, что вы и делаете в приведенном примере. Если отмена через CancellationTokenSource не срабатывает по какой-то причине - поставьте брейкпоинты или навтыкайте логов через каждую строку, найдите проблемное место, и уже исходя из этой информации решайте.
Исходя из вашей проблемы, для начала после отмены можно просто подождать завершения таска:
// Метод в котором останавливаете анимацию
cancellationTokenSource3.Cancel();
await th_Image3;

или
// Метод в котором останавливаете анимацию
cancellationTokenSource3.Cancel();
th_Image3.GetAwaiter().GetResult();

Так вы по-крайней мере должны избавиться от наложения тасков друг на друга.

Далее, таски можно чуть-чуть проще запускать, вот так:
th_Image3 = Task.Run(() => 
{ 
    // ......
});


Кроме того, если вам действительно нужно, чтобы код внутри Task.Run всегда выполнялся только в единственном экземпляре - это значит, что вам нужна критическая секция. В данном случае можно сделать при помощи семафоров:
// ......................
Semaphore _semaphore = new Semaphore(1, 1);
// ......................
th_Image3 = Task.Run(() =>
{
    _semaphore.WaitOne();

    try
    {
        // Показ GIF
    }
    finally
    {
        _semaphore.Release();
    }
});


Или же можно использовать метод ContinueWith, чтобы организовать какое-то подобие очереди задач:
// .................................................
Task th_Image3 = Task.FromResult(0);
// .................................................
th_Image3 = th_Image3.ContinueWith(() => 
{
    // Отображение GIF
});


В общем много чего можно сделать, проще всего не изобретать велосипед и взять готовое решение.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
@NikolayKhablenko Автор вопроса
И еще, по поводу анимации, она должна запуститься и выполняться, пока я не нажму кнопку к след анимации, поэтому зациклено while
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы