ImageAnimator.StopAnimate (), похоже, на самом деле ничего не делает - PullRequest
1 голос
/ 16 мая 2011

Я впервые работаю с объектом ImageAnimator и обнаружил, что вызов StopAnimate (), по-видимому, не останавливает выполнение базового потока и не вызывает обработчик FrameChanged.

Я создал тест, чтобы проиллюстрировать это, и мне интересно, может ли кто-нибудь с опытом работы с ImageAnimator пролить немного света на ТО, ЧТО на самом деле должен делать метод StopAnimate (). MSDN говорит, что должно остановить анимацию, но это не так.

Я понимаю, что могу использовать bool для управления обработкой обновлений или нет, но это обходной путь и делает (если это работает) метод StopAnimate () избыточным.

Вот код тестирования, внешние ресурсы не требуются:

using System;
using System.IO;
using System.Threading;
using System.Drawing;

namespace ImageAnimatorDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            AnimateGif();
        }

        private static void AnimateGif()
        {
            Image testImage = null;
            using (MemoryStream stream = new MemoryStream(Convert.FromBase64String(GifData)))
            {
                testImage = Image.FromStream(stream);
            }

            ImageAnimator.Animate(testImage, delegate(object o, EventArgs args)
            {
                //  Each time the frame is updated this will fire
                Console.WriteLine("Tick");
            });

            Thread.Sleep(750);

            //  The following code should stop the "animation" for the supplied image which I would
            //  THINK would stop the frame changed callback from firing...
            Console.WriteLine("Stop requested, should not see more \"Ticks\"...");
            ImageAnimator.StopAnimate(testImage, delegate(object o, EventArgs args)
            {
                Console.WriteLine("Stopped");
            });

            Console.ReadLine();
        }

        private static string GifData =
            "R0lGODlhEAAQAPQAAP///wAAAPDw8IqKiuDg4EZGRnp6egAAAFhYWCQkJKysrL6+vhQUFJycnAQEBDY2Nmh" +
            "oaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAw" +
            "EAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAAFdyAgAgIJIeWoA" +
            "kRCCMdBkKtIHIngyMKsErPBYbADpkSCwhDmQCBethRB6Vj4kFCkQPG4IlWDgrNRIwnO4UKBXDufzQvDMaoS" +
            "DBgFb886MiQadgNABAokfCwzBA8LCg0Egl8jAggGAA1kBIA1BAYzlyILczULC2UhACH5BAkKAAAALAAAAAA" +
            "QABAAAAV2ICACAmlAZTmOREEIyUEQjLKKxPHADhEvqxlgcGgkGI1DYSVAIAWMx+lwSKkICJ0QsHi9RgKBwn" +
            "VTiRQQgwF4I4UFDQQEwi6/3YSGWRRmjhEETAJfIgMFCnAKM0KDV4EEEAQLiF18TAYNXDaSe3x6mjidN1s3I" +
            "QAh+QQJCgAAACwAAAAAEAAQAAAFeCAgAgLZDGU5jgRECEUiCI+yioSDwDJyLKsXoHFQxBSHAoAAFBhqtMJg" +
            "8DgQBgfrEsJAEAg4YhZIEiwgKtHiMBgtpg3wbUZXGO7kOb1MUKRFMysCChAoggJCIg0GC2aNe4gqQldfL4l" +
            "/Ag1AXySJgn5LcoE3QXI3IQAh+QQJCgAAACwAAAAAEAAQAAAFdiAgAgLZNGU5joQhCEjxIssqEo8bC9BRjy" +
            "9Ag7GILQ4QEoE0gBAEBcOpcBA0DoxSK/e8LRIHn+i1cK0IyKdg0VAoljYIg+GgnRrwVS/8IAkICyosBIQpB" +
            "AMoKy9dImxPhS+GKkFrkX+TigtLlIyKXUF+NjagNiEAIfkECQoAAAAsAAAAABAAEAAABWwgIAICaRhlOY4E" +
            "IgjH8R7LKhKHGwsMvb4AAy3WODBIBBKCsYA9TjuhDNDKEVSERezQEL0WrhXucRUQGuik7bFlngzqVW9LMl9" +
            "XWvLdjFaJtDFqZ1cEZUB0dUgvL3dgP4WJZn4jkomWNpSTIyEAIfkECQoAAAAsAAAAABAAEAAABX4gIAICuS" +
            "xlOY6CIgiD8RrEKgqGOwxwUrMlAoSwIzAGpJpgoSDAGifDY5kopBYDlEpAQBwevxfBtRIUGi8xwWkDNBCIw" +
            "mC9Vq0aiQQDQuK+VgQPDXV9hCJjBwcFYU5pLwwHXQcMKSmNLQcIAExlbH8JBwttaX0ABAcNbWVbKyEAIfkE" +
            "CQoAAAAsAAAAABAAEAAABXkgIAICSRBlOY7CIghN8zbEKsKoIjdFzZaEgUBHKChMJtRwcWpAWoWnifm6ESA" +
            "MhO8lQK0EEAV3rFopIBCEcGwDKAqPh4HUrY4ICHH1dSoTFgcHUiZjBhAJB2AHDykpKAwHAwdzf19KkASIPl" +
            "9cDgcnDkdtNwiMJCshACH5BAkKAAAALAAAAAAQABAAAAV3ICACAkkQZTmOAiosiyAoxCq+KPxCNVsSMRgBs" +
            "iClWrLTSWFoIQZHl6pleBh6suxKMIhlvzbAwkBWfFWrBQTxNLq2RG2yhSUkDs2b63AYDAoJXAcFRwADeAkJ" +
            "DX0AQCsEfAQMDAIPBz0rCgcxky0JRWE1AmwpKyEAIfkECQoAAAAsAAAAABAAEAAABXkgIAICKZzkqJ4nQZx" +
            "LqZKv4NqNLKK2/Q4Ek4lFXChsg5ypJjs1II3gEDUSRInEGYAw6B6zM4JhrDAtEosVkLUtHA7RHaHAGJQEjs" +
            "ODcEg0FBAFVgkQJQ1pAwcDDw8KcFtSInwJAowCCA6RIwqZAgkPNgVpWndjdyohACH5BAkKAAAALAAAAAAQA" +
            "BAAAAV5ICACAimc5KieLEuUKvm2xAKLqDCfC2GaO9eL0LABWTiBYmA06W6kHgvCqEJiAIJiu3gcvgUsscHU" +
            "ERm+kaCxyxa+zRPk0SgJEgfIvbAdIAQLCAYlCj4DBw0IBQsMCjIqBAcPAooCBg9pKgsJLwUFOhCZKyQDA3Y" +
            "qIQAh+QQJCgAAACwAAAAAEAAQAAAFdSAgAgIpnOSonmxbqiThCrJKEHFbo8JxDDOZYFFb+A41E4H4OhkOip" +
            "XwBElYITDAckFEOBgMQ3arkMkUBdxIUGZpEb7kaQBRlASPg0FQQHAbEEMGDSVEAA1QBhAED1E0NgwFAooCD" +
            "WljaQIQCE5qMHcNhCkjIQAh+QQJCgAAACwAAAAAEAAQAAAFeSAgAgIpnOSoLgxxvqgKLEcCC65KEAByKK8c" +
            "SpA4DAiHQ/DkKhGKh4ZCtCyZGo6F6iYYPAqFgYy02xkSaLEMV34tELyRYNEsCQyHlvWkGCzsPgMCEAY7Cg0" +
            "4Uk48LAsDhRA8MVQPEF0GAgqYYwSRlycNcWskCkApIyEAOwAAAAAAAAAAAA==";
    }
}

Ответы [ 3 ]

3 голосов
/ 13 июля 2011

Я декомпилировал класс и обнаружил, что ImageAnimator запускает рабочий поток:

 /// <devdoc> 
    ///     Worker thread procedure which implements the main animation loop.
    ///     NOTE: This is the ONLY code the worker thread executes, keeping it in one method helps better understand
    ///     any synchronization issues.
    ///     WARNING: Also, this is the only place where ImageInfo objects (not the contained image object) are modified, 
    ///     so no access synchronization is required to modify them.
    /// </devdoc> 
    [SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals")] 
    static void AnimateImages50ms() {
        Debug.Assert(imageInfoList != null, "Null images list"); 

        while( true ) {
            // Acquire reader-lock to access imageInfoList, elemens in the list can be modified w/o needing a writer-lock.
            // Observe that we don't need to check if the thread is waiting or a writer lock here since the thread this 
            // method runs in never acquires a writer lock.
            rwImgListLock.AcquireReaderLock(Timeout.Infinite); 
            try { 
                for (int i=0;i < imageInfoList.Count; i++) {
                    ImageInfo imageInfo = imageInfoList[i]; 

                    // Frame delay is measured in 1/100ths of a second. This thread
                    // sleeps for 50 ms = 5/100ths of a second between frame updates,
                    // so we increase the frame delay count 5/100ths of a second 
                    // at a time.
                    // 
                    imageInfo.FrameTimer += 5; 
                    if (imageInfo.FrameTimer >= imageInfo.FrameDelay(imageInfo.Frame)) {
                        imageInfo.FrameTimer = 0; 

                        if (imageInfo.Frame + 1 < imageInfo.FrameCount) {
                            imageInfo.Frame++;
                        } 
                        else {
                            imageInfo.Frame = 0; 
                        } 

                        if( imageInfo.FrameDirty ){ 
                            anyFrameDirty = true;
                        }
                    }
                } 
            }
            finally { 
                rwImgListLock.ReleaseReaderLock(); 
            }

            Thread.Sleep(50);
        }
    }

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

0 голосов
/ 15 марта 2019

Причина, по которой ваш делегат не отписался, заключается в том, что ImageAnimator сравнивает адрес делегата с адресом делегата, на которого вы изначально подписаны. Делегат, которого вы передали StopAnimate, не тот делегат, которого вы передали Animate.

Это соответствующий фрагмент кода в StopAnimate:

try {
     // Find the corresponding reference and remove it
     for(int i = 0; i < imageInfoList.Count; i++) {
         ImageInfo imageInfo = imageInfoList[i];

         if(image == imageInfo.Image) {
             if((onFrameChangedHandler == imageInfo.FrameChangedHandler) || (onFrameChangedHandler != null && onFrameChangedHandler.Equals(imageInfo.FrameChangedHandler))) {
                            imageInfoList.Remove(imageInfo);
              }
                  break;
              }
          }
}

Обратите внимание на сравнение onFrameChangedHandler.

ImageAnimator использует один поток, поэтому все анимированные изображения в вашем приложении могут быть анимированы одним потоком. Если вы уничтожите рабочий поток, все анимированные изображения в вашем приложении прекратят анимацию. Это может быть хорошо, если у вас есть только один, но, вероятно, не то, что вы хотите, в большинстве случаев.

Animate и StopAnimate работают нормально; вам просто нужно правильно их назвать.

0 голосов
/ 18 января 2015

У меня была проблема, из-за которой элемент управления PictureBox с анимированным изображением GIF вызывал зависание при выходе из приложения.

Виновным был статический класс ImageAnimator, создающий поток, находящийся в цикле while (true).

К счастью, ImageAnimator устанавливает используемый поток в закрытое поле. Доступ к нему можно получить с помощью отражения и прервать, а также остановить анимацию.

Вот мой код, который я подтвердил, прерывает поток, анимация останавливается.


    try
    {
       var type = typeof(ImageAnimator);
       var info = type.GetField("animationThread", BindingFlags.NonPublic | BindingFlags.Static);
       if (info != null)
       {
           var value = info.GetValue(null);
           var thread = value as Thread;
           if (thread != null && thread.IsAlive)
           {
                thread.Abort();
           }
        }
    }
    catch{}
...