BackgroundWorker blokuje UI – .NET – Fórum – Programujte.com
 x   TIP: Přetáhni ikonu na hlavní panel pro připnutí webu

BackgroundWorker blokuje UI – .NET – Fórum – Programujte.comBackgroundWorker blokuje UI – .NET – Fórum – Programujte.com

 

Toto vlákno bylo označeno za vyřešené.
ondrej39+1
Věrný člen
14. 6. 2015   #1
-
0
-

Ahoj,

v rámci semestrální práce mám porovnat metody pro asynchronní vykonávání operací v C#. Mohl by mi někdo ze šikovných C#ářů povědět, co mám v kódu blbě, že mi BackgroundWorker blokuje uživatelské rozhraní?

Udělal jsem si třídu pro zabalelní práce pomocí různých technik...

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows.Forms;

namespace PdaSemPrace
{
    class AsynchronniOperace
    {
        private static BackgroundWorker    _worker;
        private static Stopwatch           _hodiny;
        private static long                _pocetOpakovani;
        private static ProgressBar         _progressBar;

        /// <summary>
        /// Parametrický konstruktor, který zaregistruje BackgroundWorker a ProgressBar.
        /// V konstruktoru se nastaví Worker, který se poté v prezenční vrstvě spustí
        /// přes RunWorkerAsync() a zruší přes CancelAsync();
        /// </summary>
        /// <param name="pB">ProgressBar z Windows Form, jehož se týká operace.</param>
        /// <param name="pocetOpakovani">Kolikrát se má projet cyklus při načtení ProgressBaru.</param>
        public AsynchronniOperace(ProgressBar pB, long pocetOpakovani)
        {
            // Singleton
            if (_worker != null || _progressBar != null || _hodiny != null)
            {
                return;
            }

            _hodiny = new Stopwatch();
            _pocetOpakovani = pocetOpakovani;
            _progressBar = pB;

            _worker = new BackgroundWorker
            {
                WorkerSupportsCancellation = true,
                WorkerReportsProgress = true
            };

            _worker.DoWork += bw_DoWork;
            _worker.ProgressChanged += bw_ProgressChanged;
            _worker.RunWorkerCompleted += bw_RunWorkerCompleted;
        }

        /// <summary>
        /// Metoda spustí pomocí Invoke načtení ProgressBaru.
        /// Metodu spouštět ve vlákně (Thread) vytvořeném v prezenční vrstvě.
        /// </summary>
        public static void VeVlakneNacistProgressBarInvoke()
        {
            _hodiny.Start();

            for (var i = 0; i < _pocetOpakovani; i++)
            {
                _progressBar.Invoke(new Action(() => _progressBar.Value = (int)(((double)i/_pocetOpakovani) * 100)));
            }

            _progressBar.Invoke(new Action(() => _progressBar.Value = 100));

            _hodiny.Stop();

            MessageBox.Show(
                @"Akce pomocí Invoke při " + _pocetOpakovani + @" opakováních trvala " + _hodiny.ElapsedMilliseconds +
                @" ms.", @"Výsledek testu");

            _hodiny.Reset();
        }

        /// <summary>
        /// Metoda spustí pomocí BeginInvoke načtení ProgressBaru.
        /// Metodu spouštět ve vlákně (Thread) vytvořeném v prezenční vrstvě.
        /// </summary>
        public static void VeVlakneNacistProgressBarBeginInvoke()
        {
            _hodiny.Start();

            for (var i = 0; i < _pocetOpakovani; i++)
            {
                _progressBar.BeginInvoke(new Action(() => _progressBar.Value = (int)(((double)i / _pocetOpakovani) * 100)));
            }

            _progressBar.BeginInvoke(new Action(() => _progressBar.Value = 100));

            _hodiny.Stop();

            MessageBox.Show(
                @"Akce pomocí BeginInvoke při " + _pocetOpakovani + @" opakováních trvala " +
                _hodiny.ElapsedMilliseconds + @" ms.", @"Výsledek testu");

            _hodiny.Reset();
        }

        /// <summary>
        /// Event, který se přiřadí do Worker.DoWork.
        /// </summary>
        private static void bw_DoWork(object sender, CancelEventArgs e)
        {
            var bw = sender as BackgroundWorker;

            _hodiny.Start();

            for (var i = 0; i < _pocetOpakovani; i++)
            {
                if (bw != null && bw.CancellationPending)
                {
                    e.Cancel = true;
                    _progressBar.Value = 0;

                    return;
                }

                if (bw != null){bw.ReportProgress((int) (((double) i/_pocetOpakovani)*100));}
            }

            if (bw != null){bw.ReportProgress(100);}
        }

        /// <summary>
        /// Event, který se přiřadí do Worker.ProgressChanged.
        /// </summary>
        private static void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            _progressBar.Value = e.ProgressPercentage;
        }

        /// <summary>
        /// Event, který se přiřadí do Worker.RunWorkerCompleted.
        /// </summary>
        private static void bw_RunWorkerCompleted(object sender, AsyncCompletedEventArgs e)
        {
            _hodiny.Stop();

            if (e.Cancelled)
            {
                MessageBox.Show(@"Akce byla zrušena.", @"Výsledek operace");
            }
            else if (e.Error != null)
            {
                MessageBox.Show(@"Během vykonávání akce došlo k chybě.", @"Výsledek operace");
            }
            else
            {
                MessageBox.Show(
                    @"Akce pomocí BackgroundWorkeru při " + _pocetOpakovani + @" opakováních trvala " +
                    _hodiny.ElapsedMilliseconds + @" ms.", @"Výsledek testu");
            }

            _hodiny.Reset();
        }

        /// <summary>
        /// Veřejná metoda pro spuštění BackgroundWorkeru.
        /// </summary>
        public static void RunWorkerAsync()
        {
            if (!_worker.IsBusy)
            {
                _worker.RunWorkerAsync();
            }
        }

        /// <summary>
        /// Veřejná metoda pro zabití BackGroundWorkeru.
        /// </summary>
        public static void CancelAsync()
        {
            if (_worker.WorkerSupportsCancellation)
            {
                _worker.CancelAsync();
            }
        }
    }
}

Ve formuláři poté inicializuji třídu přes

var operace = new AsynchronniOperace(progressBar1, 10000000);

progressBar1 je ProgressBar na mém formuláři. Akce normálně proběhne, ale blokuje mi UI (nemůžu akci zrušit), netuším proč.

Připojen obrázek.

Děkuji za nápady.

Nahlásit jako SPAM
IP: 46.39.172.–
Inject all the dependencies!
ondrej39+1
Věrný člen
14. 6. 2015   #2
-
0
-

Tak jsem problém zřejmě, po asi hodině zkoumání, vyřešil.

Pokud se bude stav nahlašovat příliš často (například 100 000x za vteřinu), tak si daná akce prostě vezme veškerý čas procesoru a zablokuje odezvu aplikace. V případě kontrolního prvku ProgressBar stejně nemá smysl reportovat nic jiného než integery o stavu ve formátu 1, 2, 3, ..., 99, 100, takže je stejně nesmysl reportovat každý krok (například nula by se nahlásila 10 000x namísto jen jednou).

Proto je třeba kód obsahující volání metody ReportProgress() zabalit do nějaké podmínky, aby se metoda tolikrát nevolala.

P.S. Tento problém blokování se týká i metody BeginInvoke, kterou lze volat na elementech. Pokud se volá mockrát za vteřinu (prý začínají být problémy kolem 1000 operací), tak vám UI zablokuje také.

Nahlásit jako SPAM
IP: 46.39.172.–
Inject all the dependencies!
Zjistit počet nových příspěvků

Přidej příspěvek

Toto téma je starší jak čtvrt roku – přidej svůj příspěvek jen tehdy, máš-li k tématu opravdu co říct!

Ano, opravdu chci reagovat → zobrazí formulář pro přidání příspěvku

×Vložení zdrojáku

×Vložení obrázku

Vložit URL obrázku Vybrat obrázek na disku
Vlož URL adresu obrázku:
Klikni a vyber obrázek z počítače:

×Vložení videa

Aktuálně jsou podporována videa ze serverů YouTube, Vimeo a Dailymotion.
×
 
Podporujeme Gravatara.
Zadej URL adresu Avatara (40 x 40 px) nebo emailovou adresu pro použití Gravatara.
Email nikam neukládáme, po získání Gravatara je zahozen.
-
Pravidla pro psaní příspěvků, používej diakritiku. ENTER pro nový odstavec, SHIFT + ENTER pro nový řádek.
Sledovat nové příspěvky (pouze pro přihlášené)
Sleduj vlákno a v případě přidání nového příspěvku o tom budeš vědět mezi prvními.
Reaguješ na příspěvek:

Uživatelé prohlížející si toto vlákno

Uživatelé on-line: 0 registrovaných, 31 hostů

 

Hostujeme u Českého hostingu       ISSN 1801-1586       ⇡ Nahoru Webtea.cz logo © 20032024 Programujte.com
Zasadilo a pěstuje Webtea.cz, šéfredaktor Lukáš Churý