C#: atestat și pregătire ONTI

C#: operații în fundal și anulare fără blocarea ferestrei

Execută un calcul C# în fundal și revino în siguranță la interfață. Folosește Task.Run, await și CancellationToken pentru o operație care poate fi anulată.

Un calcul lung în Click blochează fereastra. Mutăm munca independentă într-un Task, păstrând accesul la controale pe firul interfeței. Exemplul calculează suma numerelor de la unu la un milion. Nu citește TextBox și nu schimbă Label în funcția de fundal; întoarce doar rezultatul.

Cum lucrezi cu exemplul

Exemplul complet se compilează ca Windows Forms App pe .NET Framework 4.8, cu C# 7.3. Deschide Lesson.csproj din arhivă în Visual Studio pe Windows. Rulează scenariile descrise mai jos; verificarea compilării nu înlocuiește testarea ferestrei și a dispozitivelor folosite.

Descarcă sursele și instrucțiunile.

Ideea și pașii de implementare

Handler-ul async void este potrivit aici fiindcă este un eveniment UI. Await suspendă handler-ul fără să blocheze bucla de mesaje. La reluare, contextul Windows Forms permite actualizarea controalelor. Tokenul de anulare este verificat periodic în buclă. Cancel solicită oprirea, iar funcția trebuie să coopereze prin verificarea tokenului.

Dezactivăm Start cât timp calculul rulează și îl reactivăm în finally. CancellationTokenSource este eliberat după încheiere. La închiderea formularului solicităm anularea și, după await, verificăm IsDisposed înainte de a accesa controalele. Calculul scurt poate termina înaintea apăsării Anulează; acesta este un rezultat normal.

Exemplu și rezultat așteptat

O rulare completă produce 500000500000. O anulare observată înainte de final afișează Anulat. Rezultatul este long, deoarece suma depășește limita int. Fereastra trebuie să poată fi mutată în timpul operației.

Schemă pentru lecția 23: Click pe firul UI; Task.Run: sumă; Token: continuă / anulează; await → rezultat în UI.Click pe firul UI → Task.Run: sumă → Token: continuă / anulează → await → rezultat în UI.Mărește schema ↗

Codul complet

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;

static class Program
{
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new LessonForm());
    }
}

class LessonForm : Form
{
readonly Button start=new Button {Left=20,Top=20,Text="Start"};
    readonly Label output=new Label {Left=20,Top=80,Width=340};
    System.Threading.CancellationTokenSource cancellation;
    public LessonForm() {
        Text="Calcul in fundal";ClientSize=new Size(400,150);
        var cancel=new Button {Left=140,Top=20,Text="Anuleaza"};
        start.Click += Start; cancel.Click += (s,e) => {if(cancellation!=null)cancellation.Cancel();};
        FormClosing += (s,e) => {if(cancellation!=null)cancellation.Cancel();};
        Controls.AddRange(new Control[] {start,cancel,output});
    }
    async void Start(object sender,EventArgs e) {
        start.Enabled=false; var source=new System.Threading.CancellationTokenSource(); cancellation=source;
        try {
            long result=await System.Threading.Tasks.Task.Run(() => {
                long sum=0;
                for(int i=1;i<=1000000;i++) {if(i%1000==0)source.Token.ThrowIfCancellationRequested();sum+=i;}
                return sum;
            },source.Token);
            if(!IsDisposed) output.Text="Suma: " + result;
        } catch(OperationCanceledException) {if(!IsDisposed)output.Text="Anulat";}
        finally {cancellation=null;source.Dispose();if(!IsDisposed)start.Enabled=true;}
    }
}

Greșeli și limite

Nu apela .Result sau .Wait din handler pentru taskul așteptat: poți bloca interfața. Nu modifica un control din funcția transmisă lui Task.Run. Nu considera Cancel o oprire forțată instantanee. Nu ascunde excepții necunoscute sub mesajul „Anulat”; OperationCanceledException are o semnificație distinctă.

Exersează

Mărește limita moderat pentru a observa anularea, fără a crea o sarcină inutil de mare. Testează pornirea, anularea și închiderea ferestrei în timpul calculului. Rezultatul așteptat este lipsa accesului la controale eliminate și posibilitatea unei noi porniri după anulare.

Continuă parcursul

Parcursul complet de 50 de lecții

Bazele C#

Lecția precedentă: C#: cronometru corect cu Timer și Stopwatch

Lecția următoare: C#: drag and drop cu tipuri și limite explicite.

Referință: documentația Microsoft.

Întrebări frecvente

Await creează automat un fir nou?

Nu. Task.Run programează aici calculul pe un fir de lucru; await așteaptă fără blocarea handler-ului.

Cancel oprește imediat orice cod?

Nu. Operația trebuie să verifice tokenul și să coopereze.

Pot schimba Label din Task.Run?

Nu direct. Întoarce rezultatul și actualizează interfața după await pe contextul UI.

Un pas mai departe

Autentifică-te pentru a păstra progresul în cont.

Exersează cu o problemă →